// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use super::{ sys::{Image, RawImage}, traits::ImageContent, ImageAccess, ImageCreateFlags, ImageDescriptorLayouts, ImageDimensions, ImageError, ImageInner, ImageLayout, ImageSubresourceLayers, ImageUsage, MipmapsCount, }; use crate::{ buffer::{Buffer, BufferContents, BufferCreateInfo, BufferError, BufferUsage, Subbuffer}, command_buffer::{ allocator::CommandBufferAllocator, AutoCommandBufferBuilder, BlitImageInfo, BufferImageCopy, CommandBufferBeginError, CopyBufferToImageInfo, ImageBlit, }, device::{Device, DeviceOwned}, format::Format, image::sys::ImageCreateInfo, memory::{ allocator::{ AllocationCreateInfo, AllocationCreationError, AllocationType, MemoryAllocatePreference, MemoryAllocator, MemoryUsage, }, is_aligned, DedicatedAllocation, }, sampler::Filter, sync::Sharing, DeviceSize, VulkanError, }; use smallvec::{smallvec, SmallVec}; use std::{ error::Error, fmt::{Display, Error as FmtError, Formatter}, hash::{Hash, Hasher}, sync::Arc, }; /// Image whose purpose is to be used for read-only purposes. You can write to the image once, /// but then you must only ever read from it. // TODO: type (2D, 3D, array, etc.) as template parameter #[derive(Debug)] pub struct ImmutableImage { inner: Arc, layout: ImageLayout, } fn has_mipmaps(mipmaps: MipmapsCount) -> bool { match mipmaps { MipmapsCount::One => false, MipmapsCount::Log2 => true, MipmapsCount::Specific(x) => x > 1, } } fn generate_mipmaps( cbb: &mut AutoCommandBufferBuilder, image: Arc, dimensions: ImageDimensions, _layout: ImageLayout, ) where Cba: CommandBufferAllocator, { for level in 1..image.mip_levels() { let src_size = dimensions .mip_level_dimensions(level - 1) .unwrap() .width_height_depth(); let dst_size = dimensions .mip_level_dimensions(level) .unwrap() .width_height_depth(); cbb.blit_image(BlitImageInfo { regions: [ImageBlit { src_subresource: ImageSubresourceLayers { mip_level: level - 1, ..image.subresource_layers() }, src_offsets: [[0; 3], src_size], dst_subresource: ImageSubresourceLayers { mip_level: level, ..image.subresource_layers() }, dst_offsets: [[0; 3], dst_size], ..Default::default() }] .into(), filter: Filter::Linear, ..BlitImageInfo::images(image.clone(), image.clone()) }) .expect("failed to blit a mip map to image!"); } } impl ImmutableImage { /// Builds an uninitialized immutable image. /// /// Returns two things: the image, and a special access that should be used for the initial /// upload to the image. pub fn uninitialized( allocator: &(impl MemoryAllocator + ?Sized), dimensions: ImageDimensions, format: Format, mip_levels: impl Into, usage: ImageUsage, flags: ImageCreateFlags, layout: ImageLayout, queue_family_indices: impl IntoIterator, ) -> Result<(Arc, Arc), ImmutableImageCreationError> { let queue_family_indices: SmallVec<[_; 4]> = queue_family_indices.into_iter().collect(); assert!(!flags.intersects(ImageCreateFlags::DISJOINT)); // TODO: adjust the code below to make this safe let raw_image = RawImage::new( allocator.device().clone(), ImageCreateInfo { flags, dimensions, format: Some(format), mip_levels: match mip_levels.into() { MipmapsCount::Specific(num) => num, MipmapsCount::Log2 => dimensions.max_mip_levels(), MipmapsCount::One => 1, }, usage, sharing: if queue_family_indices.len() >= 2 { Sharing::Concurrent(queue_family_indices) } else { Sharing::Exclusive }, ..Default::default() }, )?; let requirements = raw_image.memory_requirements()[0]; let res = unsafe { allocator.allocate_unchecked( requirements, AllocationType::NonLinear, AllocationCreateInfo { usage: MemoryUsage::DeviceOnly, allocate_preference: MemoryAllocatePreference::Unknown, _ne: crate::NonExhaustive(()), }, Some(DedicatedAllocation::Image(&raw_image)), ) }; match res { Ok(alloc) => { debug_assert!(is_aligned(alloc.offset(), requirements.layout.alignment())); debug_assert!(alloc.size() == requirements.layout.size()); let inner = Arc::new( unsafe { raw_image.bind_memory_unchecked([alloc]) } .map_err(|(err, _, _)| err)?, ); let image = Arc::new(ImmutableImage { inner, layout }); let init = Arc::new(ImmutableImageInitialization { image: image.clone(), }); Ok((image, init)) } Err(err) => Err(err.into()), } } /// Construct an ImmutableImage from the contents of `iter`. /// /// This is a convenience function, equivalent to creating a `CpuAccessibleBuffer`, writing /// `iter` to it, then calling [`from_buffer`](ImmutableImage::from_buffer) to copy the data /// over. pub fn from_iter( allocator: &(impl MemoryAllocator + ?Sized), iter: I, dimensions: ImageDimensions, mip_levels: MipmapsCount, format: Format, command_buffer_builder: &mut AutoCommandBufferBuilder, ) -> Result, ImmutableImageCreationError> where Px: BufferContents, I: IntoIterator, I::IntoIter: ExactSizeIterator, A: CommandBufferAllocator, { let source = Buffer::from_iter( allocator, BufferCreateInfo { usage: BufferUsage::TRANSFER_SRC, ..Default::default() }, AllocationCreateInfo { usage: MemoryUsage::Upload, ..Default::default() }, iter, ) .map_err(|err| match err { BufferError::AllocError(err) => err, // We don't use sparse-binding, concurrent sharing or external memory, therefore the // other errors can't happen. _ => unreachable!(), })?; ImmutableImage::from_buffer( allocator, source, dimensions, mip_levels, format, command_buffer_builder, ) } /// Construct an ImmutableImage containing a copy of the data in `source`. /// /// This is a convenience function, equivalent to calling /// [`uninitialized`](ImmutableImage::uninitialized) with the queue family index of /// `command_buffer_builder`, then recording a `copy_buffer_to_image` command to /// `command_buffer_builder`. /// /// `command_buffer_builder` can then be used to record other commands, built, and executed as /// normal. If it is not executed, the image contents will be left undefined. pub fn from_buffer( allocator: &(impl MemoryAllocator + ?Sized), source: Subbuffer, dimensions: ImageDimensions, mip_levels: MipmapsCount, format: Format, command_buffer_builder: &mut AutoCommandBufferBuilder, ) -> Result, ImmutableImageCreationError> where A: CommandBufferAllocator, { let region = BufferImageCopy { image_subresource: ImageSubresourceLayers::from_parameters( format, dimensions.array_layers(), ), image_extent: dimensions.width_height_depth(), ..Default::default() }; let required_size = region.buffer_copy_size(format); if source.size() < required_size { return Err(ImmutableImageCreationError::SourceTooSmall { source_size: source.size(), required_size, }); } let need_to_generate_mipmaps = has_mipmaps(mip_levels); let usage = ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED | if need_to_generate_mipmaps { ImageUsage::TRANSFER_SRC } else { ImageUsage::empty() }; let flags = ImageCreateFlags::empty(); let layout = ImageLayout::ShaderReadOnlyOptimal; let (image, initializer) = ImmutableImage::uninitialized( allocator, dimensions, format, mip_levels, usage, flags, layout, source .device() .active_queue_family_indices() .iter() .copied(), )?; command_buffer_builder .copy_buffer_to_image(CopyBufferToImageInfo { regions: smallvec![region], ..CopyBufferToImageInfo::buffer_image(source, initializer) }) .unwrap(); if need_to_generate_mipmaps { generate_mipmaps( command_buffer_builder, image.clone(), image.inner.dimensions(), ImageLayout::ShaderReadOnlyOptimal, ); } Ok(image) } } unsafe impl DeviceOwned for ImmutableImage { #[inline] fn device(&self) -> &Arc { self.inner.device() } } unsafe impl ImageAccess for ImmutableImage { #[inline] fn inner(&self) -> ImageInner<'_> { ImageInner { image: &self.inner, first_layer: 0, num_layers: self.inner.dimensions().array_layers(), first_mipmap_level: 0, num_mipmap_levels: self.inner.mip_levels(), } } #[inline] fn is_layout_initialized(&self) -> bool { true } #[inline] fn initial_layout_requirement(&self) -> ImageLayout { self.layout } #[inline] fn final_layout_requirement(&self) -> ImageLayout { self.layout } #[inline] fn descriptor_layouts(&self) -> Option { Some(ImageDescriptorLayouts { storage_image: ImageLayout::General, combined_image_sampler: self.layout, sampled_image: self.layout, input_attachment: self.layout, }) } } unsafe impl

ImageContent

for ImmutableImage { fn matches_format(&self) -> bool { true // FIXME: } } impl PartialEq for ImmutableImage { #[inline] fn eq(&self, other: &Self) -> bool { self.inner() == other.inner() } } impl Eq for ImmutableImage {} impl Hash for ImmutableImage { fn hash(&self, state: &mut H) { self.inner().hash(state); } } // Must not implement Clone, as that would lead to multiple `used` values. pub struct ImmutableImageInitialization { image: Arc, } unsafe impl DeviceOwned for ImmutableImageInitialization { #[inline] fn device(&self) -> &Arc { self.image.device() } } unsafe impl ImageAccess for ImmutableImageInitialization { #[inline] fn inner(&self) -> ImageInner<'_> { self.image.inner() } #[inline] fn initial_layout_requirement(&self) -> ImageLayout { ImageLayout::Undefined } #[inline] fn final_layout_requirement(&self) -> ImageLayout { self.image.layout } #[inline] fn descriptor_layouts(&self) -> Option { None } } impl PartialEq for ImmutableImageInitialization { #[inline] fn eq(&self, other: &Self) -> bool { self.inner() == other.inner() } } impl Eq for ImmutableImageInitialization {} impl Hash for ImmutableImageInitialization { fn hash(&self, state: &mut H) { self.inner().hash(state); } } /// Error that can happen when creating an `ImmutableImage`. #[derive(Clone, Debug)] pub enum ImmutableImageCreationError { ImageCreationError(ImageError), AllocError(AllocationCreationError), CommandBufferBeginError(CommandBufferBeginError), /// The size of the provided source data is less than the required size for an image with the /// given format and dimensions. SourceTooSmall { source_size: DeviceSize, required_size: DeviceSize, }, } impl Error for ImmutableImageCreationError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::ImageCreationError(err) => Some(err), Self::AllocError(err) => Some(err), Self::CommandBufferBeginError(err) => Some(err), _ => None, } } } impl Display for ImmutableImageCreationError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::ImageCreationError(err) => err.fmt(f), Self::AllocError(err) => err.fmt(f), Self::CommandBufferBeginError(err) => err.fmt(f), Self::SourceTooSmall { source_size, required_size, } => write!( f, "the size of the provided source data ({} bytes) is less than the required size \ for an image of the given format and dimensions ({} bytes)", source_size, required_size, ), } } } impl From for ImmutableImageCreationError { fn from(err: ImageError) -> Self { Self::ImageCreationError(err) } } impl From for ImmutableImageCreationError { fn from(err: AllocationCreationError) -> Self { Self::AllocError(err) } } impl From for ImmutableImageCreationError { fn from(err: VulkanError) -> Self { Self::AllocError(err.into()) } } impl From for ImmutableImageCreationError { fn from(err: CommandBufferBeginError) -> Self { Self::CommandBufferBeginError(err) } }