use assetpath::AssetPath; use crate::prelude::*; use anyhow::Result; use std::cmp; use std::sync::{Arc, Mutex}; use std::time::Duration; enum ImageSourceType { Empty, Raw(Vec), Array(Vec>), } struct ImageCreateInfo { vk_image_create_info: VkImageCreateInfo, source_type: ImageSourceType, } impl ImageCreateInfo { fn default(source_type: ImageSourceType) -> Self { ImageCreateInfo { vk_image_create_info: VkImageCreateInfo::new( 0, VK_IMAGE_TYPE_2D, VK_FORMAT_UNDEFINED, VkExtent3D { width: 0, height: 0, depth: 0, }, 1, 1, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_SHARING_MODE_EXCLUSIVE, &[], VK_IMAGE_LAYOUT_UNDEFINED, ), source_type, } } } struct PreinitializedImage { image: VkImage, format: VkFormat, width: u32, height: u32, layers: u32, sample_count: VkSampleCountFlagBits, layout: VkImageLayout, usage: VkImageUsageFlagBits, assume_layout: bool, } enum ImageBuilderInternalType { PreinitializedImage(PreinitializedImage), NewImage(ImageCreateInfo), } /// Implements the builder pattern for Image pub struct ImageBuilder { file_name: Option, builder_type: ImageBuilderInternalType, components: VkComponentMapping, view_type: VkImageViewType, subresource_range: VkImageSubresourceRange, sampler: Option>, } impl ImageBuilder { /// Sets up the ImageBuilder for further use fn new(internal_type: ImageBuilderInternalType) -> Self { ImageBuilder { file_name: None, builder_type: internal_type, components: VkComponentMapping::default(), subresource_range: VkImageSubresourceRange { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), baseMipLevel: 0, levelCount: 1, baseArrayLayer: 0, layerCount: 1, }, view_type: VK_IMAGE_VIEW_TYPE_2D, sampler: None, } } pub fn build(self, device: &Arc, queue: &Arc>) -> Result> { let mut image_view_ci = self.vk_image_view_create_info(); match self.builder_type { ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => { image_view_ci.image = preinitialized_image.image; let image_view = device.create_image_view(&image_view_ci)?; let image = Arc::new(Image { device: device.clone(), queue: queue.clone(), image: preinitialized_image.image, image_view, _memory: None, attached: true, sampler: self.sampler, file_name: self.file_name, format: preinitialized_image.format, image_layout: Mutex::new(VK_IMAGE_LAYOUT_UNDEFINED), aspect_mask: self.subresource_range.aspectMask, width: preinitialized_image.width, height: preinitialized_image.height, layers: preinitialized_image.layers, levels: 1, sample_count: preinitialized_image.sample_count, _usage: preinitialized_image.usage, }); // TODO: check necessity if !preinitialized_image.assume_layout { if preinitialized_image.layout != VK_IMAGE_LAYOUT_UNDEFINED { Image::convert_layout(&image, preinitialized_image.layout)?; } } else { *image.image_layout.lock().unwrap() = preinitialized_image.layout; } Ok(image) } ImageBuilderInternalType::NewImage(ref info) => match info.source_type { ImageSourceType::Array(ref array) => { let arc_image = Self::create_from_source( device, queue, info, self.sampler, image_view_ci, self.file_name, )?; copy_images_to_imagearray(device, queue, &arc_image, array)?; Ok(arc_image) } ImageSourceType::Raw(ref raw) => { let arc_image = Self::create_from_source( device, queue, info, self.sampler, image_view_ci, self.file_name, )?; Self::optimize_fill(device, queue, raw, &arc_image)?; Ok(arc_image) } ImageSourceType::Empty => { let arc_image = Self::create_from_source( device, queue, info, self.sampler, image_view_ci, self.file_name, )?; Ok(arc_image) } }, } } pub fn check_configuration(&self, device: &Arc) -> bool { match &self.builder_type { ImageBuilderInternalType::NewImage(create_info) => Image::check_configuration( device, create_info.vk_image_create_info.tiling, create_info.vk_image_create_info.format, create_info.vk_image_create_info.usage, ), _ => false, } } pub fn view_type(mut self, view_type: VkImageViewType) -> Self { self.view_type = view_type; self } pub fn component_swizzle( mut self, r: VkComponentSwizzle, g: VkComponentSwizzle, b: VkComponentSwizzle, a: VkComponentSwizzle, ) -> Self { self.components.r = r; self.components.g = g; self.components.b = b; self.components.a = a; self } pub fn update_data(mut self, data: Vec) -> Self { match self.builder_type { ImageBuilderInternalType::NewImage(ref mut info) => match info.source_type { ImageSourceType::Raw(ref mut old_data) => *old_data = data, _ => panic!("wrong source type in ImageBuilder"), }, _ => panic!("wrong builder type in ImageBuilder"), } self } pub fn format(mut self, format: VkFormat) -> Self { match &mut self.builder_type { ImageBuilderInternalType::NewImage(info) => { info.vk_image_create_info.format = format; } ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => { preinitialized_image.format = format; } } self } pub fn sample_count(mut self, sample_count: impl Into) -> Self { match &mut self.builder_type { ImageBuilderInternalType::NewImage(info) => { info.vk_image_create_info.samples = sample_count.into(); } ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => { preinitialized_image.sample_count = sample_count.into(); } } self } pub fn add_usage(mut self, usage: T) -> Self where T: Into, { match self.builder_type { ImageBuilderInternalType::NewImage(ref mut info) => { info.vk_image_create_info.usage |= usage.into(); } _ => panic!("wrong builder type in ImageBuilder"), } self } pub fn flags(mut self, flags: T) -> Self where T: Into, { match self.builder_type { ImageBuilderInternalType::NewImage(ref mut info) => { info.vk_image_create_info.flags = flags.into(); } _ => panic!("wrong builder type in ImageBuilder"), } self } pub fn array_layers(mut self, layers: u32) -> Self { match &mut self.builder_type { ImageBuilderInternalType::NewImage(info) => { info.vk_image_create_info.arrayLayers = layers; self.subresource_range.layerCount = layers; } ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => { preinitialized_image.layers = layers; } } self } pub fn attach_sampler(mut self, sampler: Arc) -> Self { self.sampler = Some(sampler); if let ImageBuilderInternalType::NewImage(ref mut info) = self.builder_type { info.vk_image_create_info.usage |= VK_IMAGE_USAGE_SAMPLED_BIT; } self } // pub fn mip_map_levels(mut self, levels: u32) -> Self { // match self.builder_type { // ImageBuilderInternalType::NewImage(ref mut info) => { // info.vk_image_create_info.mipLevels = levels; // self.subresource_range.levelCount = levels; // info.vk_image_create_info.usage |= // VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; // if let Some(ref mut sampler) = self.sampler_info { // sampler.maxLod = levels as f32; // } // } // _ => panic!("wrong builder type in ImageBuilder"), // } // self // } pub fn max_mip_map_levels(mut self) -> Self { match self.builder_type { ImageBuilderInternalType::NewImage(ref mut info) => { let levels = Self::calc_mip_map_levels( info.vk_image_create_info.extent.width, info.vk_image_create_info.extent.height, ); info.vk_image_create_info.mipLevels = levels; self.subresource_range.levelCount = levels; info.vk_image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } _ => panic!("wrong builder type in ImageBuilder"), } self } pub fn aspect_mask(mut self, mask: VkImageAspectFlags) -> Self { self.subresource_range.aspectMask = mask.into(); self } fn calc_mip_map_levels(width: u32, height: u32) -> u32 { 1 + (cmp::max(width, height) as f32).log2().floor() as u32 } fn vk_image_view_create_info(&self) -> VkImageViewCreateInfo { VkImageViewCreateInfo::new( 0, VkImage::NULL_HANDLE, self.view_type, match &self.builder_type { ImageBuilderInternalType::NewImage(info) => info.vk_image_create_info.format, ImageBuilderInternalType::PreinitializedImage(preinitialized_image) => { preinitialized_image.format } }, self.components.clone(), self.subresource_range.clone(), ) } fn create_from_source( device: &Arc, queue: &Arc>, info: &ImageCreateInfo, sampler: Option>, mut view_ci: VkImageViewCreateInfo, file_name: Option, ) -> Result> { let format = view_ci.format; let (image, memory) = Self::create_texture(device, &info.vk_image_create_info)?; view_ci.image = image; let image_view = device.create_image_view(&view_ci)?; Ok(Arc::new(Image { device: device.clone(), queue: queue.clone(), image, image_view, attached: false, _memory: Some(memory), sampler, file_name, format, image_layout: Mutex::new(info.vk_image_create_info.initialLayout), aspect_mask: view_ci.subresourceRange.aspectMask, width: info.vk_image_create_info.extent.width, height: info.vk_image_create_info.extent.height, layers: info.vk_image_create_info.arrayLayers, levels: info.vk_image_create_info.mipLevels, sample_count: info.vk_image_create_info.samples, _usage: info.vk_image_create_info.usage, })) } fn create_texture( device: &Arc, image_ci: &VkImageCreateInfo, ) -> Result<(VkImage, Arc>)> { let image = Self::create_image(device, image_ci)?; let memory = Memory::image_memory( device, image, MemoryUsage::into_vma(Some(MemoryUsage::GpuOnly)), )?; Ok((image, memory)) } fn create_image(device: &Arc, image_ci: &VkImageCreateInfo) -> Result { device.create_image(image_ci) } fn optimize_fill( device: &Arc, queue: &Arc>, data: &[u8], image: &Arc, ) -> Result<()> { let staging_buffer = Buffer::builder() .set_usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT) .set_memory_usage(MemoryUsage::CpuToGpu) .set_data(data) .build(device.clone())?; copy_buffer_to_image(device, queue, &staging_buffer, image)?; Ok(()) } } /// Wrapper type around VkImage /// /// handles VkImage, VkSampler, VkDeviceSize and VkImageView internally /// just as you set it up to #[derive(Debug)] pub struct Image { // device handle device: Arc, // queue handle queue: Arc>, file_name: Option, // image handle attached: bool, image: VkImage, // image_view image_view: VkImageView, // optional handles _memory: Option>>, sampler: Option>, // image information format: VkFormat, image_layout: Mutex, aspect_mask: VkImageAspectFlagBits, width: u32, height: u32, layers: u32, // array layers levels: u32, // mip map levels sample_count: VkSampleCountFlagBits, _usage: VkImageUsageFlagBits, } impl Image { /// Creates an `ImageBuilder` where you can define the image for your needs /// /// For example, this is used to wrap swapchain images /// /// # Arguments /// /// * `image` - valid VkImage handle /// * `format` - format of this image pub fn from_preinitialized( image: VkImage, format: VkFormat, width: u32, height: u32, layout: VkImageLayout, usage: impl Into, assume_layout: bool, ) -> ImageBuilder { ImageBuilder::new(ImageBuilderInternalType::PreinitializedImage( PreinitializedImage { image, format, width, height, layers: 1, sample_count: VK_SAMPLE_COUNT_1_BIT.into(), layout, usage: usage.into(), assume_layout, }, )) } /// Creates an `ImageBuilder` where you can define the image for your needs /// /// takes the image data in form of an Vec and sets up the `ImageBuilder` /// for further configuration /// /// # Arguments /// /// * `source` - The color information for the image /// * `width` - The target width of the image /// * `height` - The target height of the image pub fn from_raw(source: Vec, width: u32, height: u32) -> ImageBuilder { let mut create_info = ImageCreateInfo::default(ImageSourceType::Raw(source)); create_info.vk_image_create_info.extent.width = width; create_info.vk_image_create_info.extent.height = height; create_info.vk_image_create_info.extent.depth = 1; create_info.vk_image_create_info.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; ImageBuilder::new(ImageBuilderInternalType::NewImage(create_info)) } /// Creates an `ImageBuilder` where you can define the image for your needs /// /// takes a path to the file and does the same as `raw_source`, but it /// extracts all needed bits from the file /// /// # Arguments /// /// * `file` - The path to the file pub fn from_file(file: AssetPath) -> Result { let texture = match image::open(&file.full_path()) { Ok(i) => i.to_rgba8(), Err(err) => return Err(anyhow::Error::new(err).context(file.full_path())), }; let (width, height) = texture.dimensions(); let mut builder = Self::from_raw(texture.into_raw(), width, height).format(VK_FORMAT_R8G8B8A8_UNORM); builder.file_name = Some(file); Ok(builder) } /// Creates an `ImageBuilder` where you can define the image for your needs /// /// takes a byte slice and does the same as `raw_source`, but it /// extracts all needed bits from the slice /// /// # Usage /// /// let mut image_builder = Image::from_slice(include_bytes!("path/to/file"))?; /// /// # Arguments /// /// * `slice` - Slice of bytes pub fn from_slice(data: &[u8]) -> Result { let texture = image::load_from_memory(data)?.to_rgba8(); let (width, height) = texture.dimensions(); Ok(Self::from_raw(texture.into_raw(), width, height).format(VK_FORMAT_R8G8B8A8_UNORM)) } /// Creates an `ImageBuilder` where you can define the image for your needs /// /// takes an array `Arc`'s and setups the `ImageBuilder` to create /// a single `Arc` with an 2d image array created from the provided images /// /// # Arguments /// /// * `array` - Source images pub fn from_array(array: Vec>) -> ImageBuilder { debug_assert!(array.is_empty(), "images array must not be empty"); let width = array[0].width(); let height = array[0].height(); if cfg!(debug_assertions) { for image in &array { if width != image.width() || height != image.height() { panic!("images are not equally sized"); } } } let array_len = array.len() as u32; let mut create_info = ImageCreateInfo::default(ImageSourceType::Array(array)); create_info.vk_image_create_info.arrayLayers = array_len; create_info.vk_image_create_info.imageType = VK_IMAGE_TYPE_2D; create_info.vk_image_create_info.extent.width = width; create_info.vk_image_create_info.extent.height = height; create_info.vk_image_create_info.extent.depth = 1; let mut image_builder = ImageBuilder::new(ImageBuilderInternalType::NewImage(create_info)); image_builder.view_type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; image_builder.subresource_range.layerCount = array_len; image_builder } /// Creates an `ImageBuilder` where you can define the image for your needs /// /// takes raw information to setup `ImageBuilder`, that creates an `Arc` /// with no color information /// /// # Arguments /// /// * `width` - The target width of the image /// * `height` - The target height of the image /// * `usage` - `VkImageUsageFlagBits` mask to define the image usage /// * `sample_count` - `VkSampleCountFlags` to define the image's sample count pub fn empty( width: u32, height: u32, usage: impl Into, sample_count: VkSampleCountFlags, ) -> ImageBuilder { let mut create_info = ImageCreateInfo::default(ImageSourceType::Empty); create_info.vk_image_create_info.samples = sample_count.into(); create_info.vk_image_create_info.extent.width = width; create_info.vk_image_create_info.extent.height = height; create_info.vk_image_create_info.extent.depth = 1; create_info.vk_image_create_info.usage = usage.into(); ImageBuilder::new(ImageBuilderInternalType::NewImage(create_info)) } pub fn check_configuration( device: &Arc, tiling: VkImageTiling, format: VkFormat, usage: impl Into, ) -> bool { let physical_device = device.physical_device(); match tiling { VK_IMAGE_TILING_OPTIMAL => physical_device.check_optimal_format_features(format, usage), VK_IMAGE_TILING_LINEAR => physical_device.check_linear_format_features(format, usage), } } pub fn device(&self) -> &Arc { &self.device } pub fn queue(&self) -> &Arc> { &self.queue } pub fn file_name(&self) -> Option<&AssetPath> { self.file_name.as_ref() } pub fn convert_layout(self: &Arc, target_layout: VkImageLayout) -> Result<()> { into_layout(self, target_layout) } pub fn vk_format(&self) -> VkFormat { self.format } pub fn sampler(&self) -> &Option> { &self.sampler } pub fn width(&self) -> u32 { self.width } pub fn height(&self) -> u32 { self.height } pub fn layers(&self) -> u32 { self.layers } pub fn levels(&self) -> u32 { self.levels } pub fn sample_count(&self) -> VkSampleCountFlagBits { self.sample_count } pub fn image_layout(&self) -> VkImageLayout { *self.image_layout.lock().unwrap() } pub(crate) fn set_image_layout(&self, layout: VkImageLayout) { *self.image_layout.lock().unwrap() = layout; } pub fn full_resource_range(&self) -> VkImageSubresourceRange { VkImageSubresourceRange { aspectMask: self.aspect_mask, baseMipLevel: 0, levelCount: self.levels, baseArrayLayer: 0, layerCount: self.layers, } } pub fn full_resource_layers(&self) -> VkImageSubresourceLayers { VkImageSubresourceLayers { aspectMask: self.aspect_mask, mipLevel: 0, baseArrayLayer: 0, layerCount: self.layers, } } pub fn src_layout_to_access(image_layout: VkImageLayout) -> VkAccessFlagBits { match image_layout { VK_IMAGE_LAYOUT_UNDEFINED => 0u32.into(), VK_IMAGE_LAYOUT_PREINITIALIZED => VK_ACCESS_HOST_WRITE_BIT.into(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL => VK_ACCESS_TRANSFER_WRITE_BIT.into(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL => VK_ACCESS_TRANSFER_READ_BIT.into(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL => VK_ACCESS_SHADER_READ_BIT.into(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL => VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT.into(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR => VK_ACCESS_MEMORY_READ_BIT.into(), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL => { VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT } VK_IMAGE_LAYOUT_GENERAL => VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, _ => unimplemented!("source image layout ({:?})", image_layout), } } pub fn dst_layout_to_access(image_layout: VkImageLayout) -> VkAccessFlagBits { match image_layout { VK_IMAGE_LAYOUT_UNDEFINED => { panic!("target image layout must not be VK_IMAGE_LAYOUT_UNDEFINED") } VK_IMAGE_LAYOUT_PREINITIALIZED => { panic!("target image layout must not be VK_IMAGE_LAYOUT_PREINITIALIZED") } VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL => VK_ACCESS_TRANSFER_WRITE_BIT.into(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL => VK_ACCESS_TRANSFER_READ_BIT.into(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL => VK_ACCESS_SHADER_READ_BIT.into(), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL => VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT.into(), VK_IMAGE_LAYOUT_PRESENT_SRC_KHR => VK_ACCESS_MEMORY_READ_BIT.into(), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL => { VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT } VK_IMAGE_LAYOUT_GENERAL => VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL => VK_ACCESS_SHADER_READ_BIT.into(), } } } impl VulkanDevice for Image { fn device(&self) -> &Arc { &self.device } } impl_vk_handle!(Image, VkImage, image); impl_vk_handle!(Image, VkImageView, image_view); impl Drop for Image { fn drop(&mut self) { self.device.destroy_image_view(self.image_view); if !self.attached { self.device.destroy_image(self.image); } } } fn into_layout(image: &Arc, layout: VkImageLayout) -> Result<()> { // create a new command buffer let command_buffer = CommandBuffer::new_primary().build(image.device.clone(), image.queue.clone())?; { // begin recording into this command buffer let mut buffer_recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, ))?; // subresource information let subresource_range = VkImageSubresourceRange { aspectMask: image.aspect_mask, baseMipLevel: 0, levelCount: image.levels(), baseArrayLayer: 0, layerCount: image.layers(), }; // change image layout buffer_recorder.set_image_layout(image, layout, subresource_range); } // submit current queue let submit = SubmitInfo::default().add_command_buffer(&command_buffer); let fence = Fence::builder().build(image.device.clone())?; image .queue .lock() .map_err(|_| anyhow::Error::msg("Failed getting vulkan device queue lock"))? .submit(Some(&fence), &[submit])?; image .device .wait_for_fences(&[&fence], true, Duration::from_secs(1))?; Ok(()) } fn copy_buffer_to_image( device: &Arc, queue: &Arc>, buffer: &Arc>, image: &Arc, ) -> Result<()> where T: Copy, { // create a new command buffer let command_buffer = CommandBuffer::new_primary().build(device.clone(), queue.clone())?; { // begin recording into this command buffer let mut buffer_recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, ))?; // copy info for copying the content of the buffer into the image let buffer_image_copy = VkBufferImageCopy { bufferOffset: 0, bufferRowLength: 0, bufferImageHeight: 0, imageSubresource: VkImageSubresourceLayers { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), mipLevel: 0, baseArrayLayer: 0, layerCount: 1, }, imageOffset: VkOffset3D { x: 0, y: 0, z: 0 }, imageExtent: VkExtent3D { width: image.width(), height: image.height(), depth: 1, }, }; // subresource information let mut subresource_range = VkImageSubresourceRange { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), baseMipLevel: 0, levelCount: image.levels(), baseArrayLayer: 0, layerCount: 1, }; // set image layout to receive content buffer_recorder.set_image_layout( image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresource_range.clone(), ); // the actual copy command buffer_recorder.copy_buffer_to_image( buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &[buffer_image_copy], ); // just transition one mip level at a time subresource_range.levelCount = 1; // mip map creation if image.levels() > 1 { blit_mip_maps( &mut buffer_recorder, image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, ); } else { // set image to be usable inside a shader buffer_recorder.set_image_layout( image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresource_range, ); } } // submit current queue let submit = SubmitInfo::default().add_command_buffer(&command_buffer); let fence = Fence::builder().build(device.clone())?; queue .lock() .map_err(|_| anyhow::Error::msg("Failed getting vulkan device queue lock"))? .submit(Some(&fence), &[submit])?; device.wait_for_fences(&[&fence], true, Duration::from_secs(1))?; Ok(()) } fn copy_images_to_imagearray( device: &Arc, queue: &Arc>, image_array: &Arc, images: &[Arc], ) -> Result<()> { // create a new command buffer let command_buffer = CommandBuffer::new_primary().build(device.clone(), queue.clone())?; { // set command buffer buffer in recording state let mut buffer_recorder = command_buffer.begin(VkCommandBufferBeginInfo::new( VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, ))?; // subresource range of the receiving image let array_subresource_range = VkImageSubresourceRange { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), baseMipLevel: 0, levelCount: image_array.levels(), baseArrayLayer: 0, layerCount: image_array.layers(), }; // set the target image into receiving state buffer_recorder.set_image_layout( image_array, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, array_subresource_range.clone(), ); for (i, image) in images.iter().enumerate() { // if source and target image have the same count of // mip maps or the source has more mip maps, // we can just copy every mip level into the // correct target level and layer if image.levels() >= image_array.levels() { for k in 0..image_array.levels() { copy_image_to_image(&mut buffer_recorder, image, image_array, k, i as u32); } // if the source image has less mip maps than the target image, // we just gonna copy the first level and blit the rest } else { copy_image_to_image(&mut buffer_recorder, image, image_array, 0, i as u32); blit_mip_maps( &mut buffer_recorder, image_array, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ); } } // set the target image into a shader usable state buffer_recorder.set_image_layout( image_array, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, array_subresource_range, ); } // submit current queue let submit = SubmitInfo::default().add_command_buffer(&command_buffer); let fence = Fence::builder().build(device.clone())?; queue .lock() .map_err(|_| anyhow::Error::msg("Failed getting vulkan device queue lock"))? .submit(Some(&fence), &[submit])?; device.wait_for_fences(&[&fence], true, Duration::from_secs(1))?; Ok(()) } fn copy_image_to_image( buffer_recorder: &mut CommandBufferRecorder<'_>, src_image: &Arc, dst_image: &Arc, mip_level: u32, dst_layer: u32, ) { // copy information to get every source into the right target slot let image_copy = VkImageCopy { srcSubresource: VkImageSubresourceLayers { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), mipLevel: mip_level, baseArrayLayer: 0, layerCount: 1, }, srcOffset: VkOffset3D { x: 0, y: 0, z: 0 }, dstSubresource: VkImageSubresourceLayers { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), mipLevel: mip_level, baseArrayLayer: dst_layer, layerCount: 1, }, dstOffset: VkOffset3D { x: 0, y: 0, z: 0 }, extent: VkExtent3D { width: src_image.width(), height: src_image.height(), depth: 1, }, }; let subresource_range = VkImageSubresourceRange { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), baseMipLevel: mip_level, levelCount: 1, baseArrayLayer: 0, layerCount: 1, }; // set the source image into sending state buffer_recorder.set_image_layout( src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, subresource_range.clone(), ); // copy the source data into the target slot buffer_recorder.copy_image( src_image, dst_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &[image_copy], ); // set the source image back to a usable state for shaders buffer_recorder.set_image_layout( src_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresource_range, ); } fn blit_mip_maps( buffer_recorder: &mut CommandBufferRecorder<'_>, image: &Arc, target_image_layout: VkImageLayout, ) { let mut mip_width = image.width(); let mut mip_height = image.height(); // subresource information let mut subresource_range = VkImageSubresourceRange { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), baseMipLevel: 0, levelCount: 1, baseArrayLayer: 0, layerCount: 1, }; for i in 1..image.levels() { let source_mip_level = i - 1; let target_mip_level = i; // transition the previous mip level from destination to source subresource_range.baseMipLevel = source_mip_level; buffer_recorder.set_image_layout( image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, subresource_range.clone(), ); // create the blit information to blit the data from one mip level to another let image_blit = VkImageBlit { srcSubresource: VkImageSubresourceLayers { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), mipLevel: source_mip_level, baseArrayLayer: 0, layerCount: 1, }, srcOffsets: [ VkOffset3D { x: 0, y: 0, z: 0 }, VkOffset3D { x: mip_width as i32, y: mip_height as i32, z: 1, }, ], dstSubresource: VkImageSubresourceLayers { aspectMask: VK_IMAGE_ASPECT_COLOR_BIT.into(), mipLevel: target_mip_level, baseArrayLayer: 0, layerCount: 1, }, dstOffsets: [ VkOffset3D { x: 0, y: 0, z: 0 }, VkOffset3D { x: if mip_width > 1 { mip_width as i32 / 2 } else { 1 }, y: if mip_height > 1 { mip_height as i32 / 2 } else { 1 }, z: 1, }, ], }; // execute the actual blit buffer_recorder.blit_image( image, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, &[image_blit], VK_FILTER_LINEAR, ); // set mip level i - 1 to target layout buffer_recorder.set_image_layout(image, target_image_layout, subresource_range.clone()); image.set_image_layout(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); mip_width = if mip_width > 1 { mip_width / 2 } else { 1 }; mip_height = if mip_height > 1 { mip_height / 2 } else { 1 }; } // set last level to be target layout subresource_range.baseMipLevel = image.levels() - 1; buffer_recorder.set_image_layout(image, target_image_layout, subresource_range); }