diff options
Diffstat (limited to 'src/image/mod.rs')
-rw-r--r-- | src/image/mod.rs | 1011 |
1 files changed, 721 insertions, 290 deletions
diff --git a/src/image/mod.rs b/src/image/mod.rs index 4e7f4af..6b8c2b6 100644 --- a/src/image/mod.rs +++ b/src/image/mod.rs @@ -46,21 +46,29 @@ //! To be written. //! -pub use self::aspect::ImageAspect; -pub use self::aspect::ImageAspects; -pub use self::attachment::AttachmentImage; -pub use self::immutable::ImmutableImage; -pub use self::layout::ImageDescriptorLayouts; -pub use self::layout::ImageLayout; -pub use self::storage::StorageImage; -pub use self::swapchain::SwapchainImage; -pub use self::sys::ImageCreationError; -pub use self::traits::ImageAccess; -pub use self::traits::ImageInner; -pub use self::usage::ImageUsage; -pub use self::view::ImageViewAbstract; -use std::cmp; -use std::convert::TryFrom; +pub use self::{ + aspect::{ImageAspect, ImageAspects}, + attachment::AttachmentImage, + immutable::ImmutableImage, + layout::{ImageDescriptorLayouts, ImageLayout}, + storage::StorageImage, + swapchain::SwapchainImage, + sys::ImageError, + traits::{ImageAccess, ImageInner}, + usage::ImageUsage, + view::{ImageViewAbstract, ImageViewType}, +}; + +#[cfg(target_os = "linux")] +pub use self::storage::SubresourceData; + +use crate::{ + format::Format, + macros::{vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum}, + memory::{ExternalMemoryHandleType, ExternalMemoryProperties}, + DeviceSize, +}; +use std::{cmp, ops::Range}; mod aspect; pub mod attachment; // TODO: make private @@ -73,40 +81,220 @@ pub mod traits; mod usage; pub mod view; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[repr(u32)] -pub enum SampleCount { - Sample1 = ash::vk::SampleCountFlags::TYPE_1.as_raw(), - Sample2 = ash::vk::SampleCountFlags::TYPE_2.as_raw(), - Sample4 = ash::vk::SampleCountFlags::TYPE_4.as_raw(), - Sample8 = ash::vk::SampleCountFlags::TYPE_8.as_raw(), - Sample16 = ash::vk::SampleCountFlags::TYPE_16.as_raw(), - Sample32 = ash::vk::SampleCountFlags::TYPE_32.as_raw(), - Sample64 = ash::vk::SampleCountFlags::TYPE_64.as_raw(), -} +vulkan_bitflags! { + #[non_exhaustive] -impl From<SampleCount> for ash::vk::SampleCountFlags { - #[inline] - fn from(val: SampleCount) -> Self { - Self::from_raw(val as u32) - } + /// Flags that can be set when creating a new image. + ImageCreateFlags = ImageCreateFlags(u32); + + /* TODO: enable + /// The image will be backed by sparse memory binding (through queue commands) instead of + /// regular binding (through [`bind_memory`]). + /// + /// The [`sparse_binding`] feature must be enabled on the device. + /// + /// [`bind_memory`]: sys::RawImage::bind_memory + /// [`sparse_binding`]: crate::device::Features::sparse_binding + SPARSE_BINDING = SPARSE_BINDING,*/ + + /* TODO: enable + /// The image can be used without being fully resident in memory at the time of use. + /// + /// This requires the `sparse_binding` flag as well. + /// + /// Depending on the image dimensions, either the [`sparse_residency_image2_d`] or the + /// [`sparse_residency_image3_d`] feature must be enabled on the device. + /// For a multisampled image, the one of the features [`sparse_residency2_samples`], + /// [`sparse_residency4_samples`], [`sparse_residency8_samples`] or + /// [`sparse_residency16_samples`], corresponding to the sample count of the image, must + /// be enabled on the device. + /// + /// [`sparse_binding`]: crate::device::Features::sparse_binding + /// [`sparse_residency_image2_d`]: crate::device::Features::sparse_residency_image2_d + /// [`sparse_residency_image2_3`]: crate::device::Features::sparse_residency_image3_d + /// [`sparse_residency2_samples`]: crate::device::Features::sparse_residency2_samples + /// [`sparse_residency4_samples`]: crate::device::Features::sparse_residency4_samples + /// [`sparse_residency8_samples`]: crate::device::Features::sparse_residency8_samples + /// [`sparse_residency16_samples`]: crate::device::Features::sparse_residency16_samples + SPARSE_RESIDENCY = SPARSE_RESIDENCY,*/ + + /* TODO: enable + /// The buffer's memory can alias with another image or a different part of the same image. + /// + /// This requires the `sparse_binding` flag as well. + /// + /// The [`sparse_residency_aliased`] feature must be enabled on the device. + /// + /// [`sparse_residency_aliased`]: crate::device::Features::sparse_residency_aliased + SPARSE_ALIASED = SPARSE_ALIASED,*/ + + /// For non-multi-planar formats, whether an image view wrapping the image can have a + /// different format. + /// + /// For multi-planar formats, whether an image view wrapping the image can be created from a + /// single plane of the image. + MUTABLE_FORMAT = MUTABLE_FORMAT, + + /// For 2D images, whether an image view of type [`ImageViewType::Cube`] or + /// [`ImageViewType::CubeArray`] can be created from the image. + /// + /// [`ImageViewType::Cube`]: crate::image::view::ImageViewType::Cube + /// [`ImageViewType::CubeArray`]: crate::image::view::ImageViewType::CubeArray + CUBE_COMPATIBLE = CUBE_COMPATIBLE, + + /* TODO: enable + // TODO: document + ALIAS = ALIAS { + api_version: V1_1, + device_extensions: [khr_bind_memory2], + },*/ + + /* TODO: enable + // TODO: document + SPLIT_INSTANCE_BIND_REGIONS = SPLIT_INSTANCE_BIND_REGIONS { + api_version: V1_1, + device_extensions: [khr_device_group], + },*/ + + /// For 3D images, whether an image view of type [`ImageViewType::Dim2d`] or + /// [`ImageViewType::Dim2dArray`] can be created from the image. + /// + /// On [portability subset] devices, the [`image_view2_d_on3_d_image`] feature must be enabled + /// on the device. + /// + /// [`ImageViewType::Dim2d`]: crate::image::view::ImageViewType::Dim2d + /// [`ImageViewType::Dim2dArray`]: crate::image::view::ImageViewType::Dim2dArray + /// [portability subset]: crate::instance#portability-subset-devices-and-the-enumerate_portability-flag + /// [`image_view2_d_on3_d_image`]: crate::device::Features::image_view2_d_on3_d_image + ARRAY_2D_COMPATIBLE = TYPE_2D_ARRAY_COMPATIBLE { + api_version: V1_1, + device_extensions: [khr_maintenance1], + }, + + /// For images with a compressed format, whether an image view with an uncompressed + /// format can be created from the image, where each texel in the view will correspond to a + /// compressed texel block in the image. + /// + /// Requires `mutable_format`. + BLOCK_TEXEL_VIEW_COMPATIBLE = BLOCK_TEXEL_VIEW_COMPATIBLE { + api_version: V1_1, + device_extensions: [khr_maintenance2], + }, + + /* TODO: enable + // TODO: document + EXTENDED_USAGE = EXTENDED_USAGE { + api_version: V1_1, + device_extensions: [khr_maintenance2], + },*/ + + /* TODO: enable + // TODO: document + PROTECTED = PROTECTED { + api_version: V1_1, + },*/ + + /// For images with a multi-planar format, whether each plane will have its memory bound + /// separately, rather than having a single memory binding for the whole image. + DISJOINT = DISJOINT { + api_version: V1_1, + device_extensions: [khr_sampler_ycbcr_conversion], + }, + + /* TODO: enable + // TODO: document + CORNER_SAMPLED = CORNER_SAMPLED_NV { + device_extensions: [nv_corner_sampled_image], + },*/ + + /* TODO: enable + // TODO: document + SAMPLE_LOCATIONS_COMPATIBLE_DEPTH = SAMPLE_LOCATIONS_COMPATIBLE_DEPTH_EXT { + device_extensions: [ext_sample_locations], + },*/ + + /* TODO: enable + // TODO: document + SUBSAMPLED = SUBSAMPLED_EXT { + device_extensions: [ext_fragment_density_map], + },*/ + + /* TODO: enable + // TODO: document + MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED = MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_EXT { + device_extensions: [ext_multisampled_render_to_single_sampled], + },*/ + + /* TODO: enable + // TODO: document + TYPE_2D_VIEW_COMPATIBLE = TYPE_2D_VIEW_COMPATIBLE_EXT { + device_extensions: [ext_image_2d_view_of_3d], + },*/ + + /* TODO: enable + // TODO: document + FRAGMENT_DENSITY_MAP_OFFSET = FRAGMENT_DENSITY_MAP_OFFSET_QCOM { + device_extensions: [qcom_fragment_density_map_offset], + },*/ } -impl TryFrom<ash::vk::SampleCountFlags> for SampleCount { - type Error = (); +vulkan_bitflags_enum! { + #[non_exhaustive] - #[inline] - fn try_from(val: ash::vk::SampleCountFlags) -> Result<Self, Self::Error> { - match val { - ash::vk::SampleCountFlags::TYPE_1 => Ok(Self::Sample1), - ash::vk::SampleCountFlags::TYPE_2 => Ok(Self::Sample2), - ash::vk::SampleCountFlags::TYPE_4 => Ok(Self::Sample4), - ash::vk::SampleCountFlags::TYPE_8 => Ok(Self::Sample8), - ash::vk::SampleCountFlags::TYPE_16 => Ok(Self::Sample16), - ash::vk::SampleCountFlags::TYPE_32 => Ok(Self::Sample32), - ash::vk::SampleCountFlags::TYPE_64 => Ok(Self::Sample64), - _ => Err(()), + /// A set of [`SampleCount`] values. + SampleCounts impl { + /// Returns the maximum sample count in `self`. + #[inline] + pub const fn max_count(self) -> SampleCount { + if self.intersects(SampleCounts::SAMPLE_64) { + SampleCount::Sample64 + } else if self.intersects(SampleCounts::SAMPLE_32) { + SampleCount::Sample32 + } else if self.intersects(SampleCounts::SAMPLE_16) { + SampleCount::Sample16 + } else if self.intersects(SampleCounts::SAMPLE_8) { + SampleCount::Sample8 + } else if self.intersects(SampleCounts::SAMPLE_4) { + SampleCount::Sample4 + } else if self.intersects(SampleCounts::SAMPLE_2) { + SampleCount::Sample2 + } else { + SampleCount::Sample1 + } } + }, + + /// The number of samples per texel of an image. + SampleCount, + + = SampleCountFlags(u32); + + /// 1 sample per texel. + SAMPLE_1, Sample1 = TYPE_1, + + /// 2 samples per texel. + SAMPLE_2, Sample2 = TYPE_2, + + /// 4 samples per texel. + SAMPLE_4, Sample4 = TYPE_4, + + /// 8 samples per texel. + SAMPLE_8, Sample8 = TYPE_8, + + /// 16 samples per texel. + SAMPLE_16, Sample16 = TYPE_16, + + /// 32 samples per texel. + SAMPLE_32, Sample32 = TYPE_32, + + /// 64 samples per texel. + SAMPLE_64, Sample64 = TYPE_64, +} + +impl From<SampleCount> for u32 { + #[inline] + fn from(value: SampleCount) -> Self { + value as u32 } } @@ -128,73 +316,10 @@ impl TryFrom<u32> for SampleCount { } } -/// Specifies how many sample counts supported for an image used for storage operations. -#[derive(Debug, Copy, Clone, Default)] -pub struct SampleCounts { - // specify an image with one sample per pixel - pub sample1: bool, - // specify an image with 2 samples per pixel - pub sample2: bool, - // specify an image with 4 samples per pixel - pub sample4: bool, - // specify an image with 8 samples per pixel - pub sample8: bool, - // specify an image with 16 samples per pixel - pub sample16: bool, - // specify an image with 32 samples per pixel - pub sample32: bool, - // specify an image with 64 samples per pixel - pub sample64: bool, -} - -impl From<ash::vk::SampleCountFlags> for SampleCounts { - fn from(sample_counts: ash::vk::SampleCountFlags) -> SampleCounts { - SampleCounts { - sample1: !(sample_counts & ash::vk::SampleCountFlags::TYPE_1).is_empty(), - sample2: !(sample_counts & ash::vk::SampleCountFlags::TYPE_2).is_empty(), - sample4: !(sample_counts & ash::vk::SampleCountFlags::TYPE_4).is_empty(), - sample8: !(sample_counts & ash::vk::SampleCountFlags::TYPE_8).is_empty(), - sample16: !(sample_counts & ash::vk::SampleCountFlags::TYPE_16).is_empty(), - sample32: !(sample_counts & ash::vk::SampleCountFlags::TYPE_32).is_empty(), - sample64: !(sample_counts & ash::vk::SampleCountFlags::TYPE_64).is_empty(), - } - } -} - -impl From<SampleCounts> for ash::vk::SampleCountFlags { - fn from(val: SampleCounts) -> ash::vk::SampleCountFlags { - let mut sample_counts = ash::vk::SampleCountFlags::default(); - - if val.sample1 { - sample_counts |= ash::vk::SampleCountFlags::TYPE_1; - } - if val.sample2 { - sample_counts |= ash::vk::SampleCountFlags::TYPE_2; - } - if val.sample4 { - sample_counts |= ash::vk::SampleCountFlags::TYPE_4; - } - if val.sample8 { - sample_counts |= ash::vk::SampleCountFlags::TYPE_8; - } - if val.sample16 { - sample_counts |= ash::vk::SampleCountFlags::TYPE_16; - } - if val.sample32 { - sample_counts |= ash::vk::SampleCountFlags::TYPE_32; - } - if val.sample64 { - sample_counts |= ash::vk::SampleCountFlags::TYPE_64; - } - - sample_counts - } -} - /// Specifies how many mipmaps must be allocated. /// /// Note that at least one mipmap must be allocated, to store the main level of the image. -#[derive(Debug, Copy, Clone)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum MipmapsCount { /// Allocates the number of mipmaps required to store all the mipmaps of the image where each /// mipmap is half the dimensions of the previous level. Guaranteed to be always supported. @@ -218,151 +343,38 @@ impl From<u32> for MipmapsCount { } } -/// Helper type for creating extents -#[derive(Debug, Copy, Clone)] -pub enum Extent { - E1D([u32; 1]), - E2D([u32; 2]), - E3D([u32; 3]), -} +vulkan_enum! { + #[non_exhaustive] -impl From<ash::vk::Extent2D> for Extent { - fn from(extent: ash::vk::Extent2D) -> Self { - Extent::E2D([extent.width, extent.height]) - } -} - -impl From<ash::vk::Extent3D> for Extent { - fn from(extent: ash::vk::Extent3D) -> Self { - Extent::E3D([extent.width, extent.height, extent.depth]) - } -} -impl TryFrom<Extent> for ash::vk::Extent2D { - type Error = (); + // TODO: document + ImageType = ImageType(i32); - fn try_from(extent: Extent) -> Result<Self, Self::Error> { - match extent { - Extent::E2D(a) => Ok(ash::vk::Extent2D { - width: a[0], - height: a[1], - }), - _ => Err(()), - } - } -} + // TODO: document + Dim1d = TYPE_1D, -impl TryFrom<Extent> for ash::vk::Extent3D { - type Error = (); + // TODO: document + Dim2d = TYPE_2D, - fn try_from(extent: Extent) -> Result<Self, Self::Error> { - match extent { - Extent::E3D(a) => Ok(ash::vk::Extent3D { - width: a[0], - height: a[1], - depth: a[2], - }), - _ => Err(()), - } - } + // TODO: document + Dim3d = TYPE_3D, } -/// Helper type returned from Device's `fn image_format_properties()` -pub struct ImageFormatProperties { - pub max_extent: Extent, - pub max_mip_levels: MipmapsCount, - pub max_array_layers: u32, - pub sample_counts: SampleCounts, - pub max_resource_size: usize, -} +vulkan_enum! { + #[non_exhaustive] -impl From<ash::vk::ImageFormatProperties> for ImageFormatProperties { - fn from(props: ash::vk::ImageFormatProperties) -> Self { - Self { - max_extent: props.max_extent.into(), - max_mip_levels: props.max_mip_levels.into(), - max_array_layers: props.max_array_layers, - sample_counts: props.sample_counts.into(), - max_resource_size: props.max_resource_size as usize, - } - } -} + // TODO: document + ImageTiling = ImageTiling(i32); -#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] -pub struct ImageCreateFlags { - pub sparse_binding: bool, - pub sparse_residency: bool, - pub sparse_aliased: bool, - pub mutable_format: bool, - pub cube_compatible: bool, - pub array_2d_compatible: bool, -} + // TODO: document + Optimal = OPTIMAL, -impl ImageCreateFlags { - pub fn all() -> Self { - Self { - sparse_binding: true, - sparse_residency: true, - sparse_aliased: true, - mutable_format: true, - cube_compatible: true, - array_2d_compatible: true, - } - } + // TODO: document + Linear = LINEAR, - pub fn none() -> Self { - Self::default() - } -} - -impl From<ImageCreateFlags> for ash::vk::ImageCreateFlags { - fn from(flags: ImageCreateFlags) -> Self { - let mut vk_flags = Self::default(); - if flags.sparse_binding { - vk_flags |= ash::vk::ImageCreateFlags::SPARSE_BINDING - }; - if flags.sparse_residency { - vk_flags |= ash::vk::ImageCreateFlags::SPARSE_RESIDENCY - }; - if flags.sparse_aliased { - vk_flags |= ash::vk::ImageCreateFlags::SPARSE_ALIASED - }; - if flags.mutable_format { - vk_flags |= ash::vk::ImageCreateFlags::MUTABLE_FORMAT - }; - if flags.cube_compatible { - vk_flags |= ash::vk::ImageCreateFlags::CUBE_COMPATIBLE - }; - if flags.array_2d_compatible { - vk_flags |= ash::vk::ImageCreateFlags::TYPE_2D_ARRAY_COMPATIBLE_KHR - }; - vk_flags - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(i32)] -pub enum ImageType { - Dim1d = ash::vk::ImageType::TYPE_1D.as_raw(), - Dim2d = ash::vk::ImageType::TYPE_2D.as_raw(), - Dim3d = ash::vk::ImageType::TYPE_3D.as_raw(), -} -impl From<ImageType> for ash::vk::ImageType { - fn from(val: ImageType) -> Self { - ash::vk::ImageType::from_raw(val as i32) - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -#[repr(i32)] -pub enum ImageTiling { - Optimal = ash::vk::ImageTiling::OPTIMAL.as_raw(), - Linear = ash::vk::ImageTiling::LINEAR.as_raw(), -} - -impl From<ImageTiling> for ash::vk::ImageTiling { - fn from(val: ImageTiling) -> Self { - ash::vk::ImageTiling::from_raw(val as i32) - } + // TODO: document + DrmFormatModifier = DRM_FORMAT_MODIFIER_EXT { + device_extensions: [ext_image_drm_format_modifier], + }, } /// The dimensions of an image. @@ -437,11 +449,20 @@ impl ImageDimensions { self.width() * self.height() * self.depth() * self.array_layers() } - /// Returns the maximum number of mipmaps for these image dimensions. + #[inline] + pub fn image_type(&self) -> ImageType { + match *self { + ImageDimensions::Dim1d { .. } => ImageType::Dim1d, + ImageDimensions::Dim2d { .. } => ImageType::Dim2d, + ImageDimensions::Dim3d { .. } => ImageType::Dim3d, + } + } + + /// Returns the maximum number of mipmap levels for these image dimensions. /// - /// The returned value is always at least superior or equal to 1. + /// The returned value is always at least 1. /// - /// # Example + /// # Examples /// /// ``` /// use vulkano::image::ImageDimensions; @@ -452,19 +473,29 @@ impl ImageDimensions { /// array_layers: 1, /// }; /// - /// assert_eq!(dims.max_mipmaps(), 6); + /// assert_eq!(dims.max_mip_levels(), 6); /// ``` - /// - pub fn max_mipmaps(&self) -> u32 { - 32 - (self.width() | self.height() | self.depth()).leading_zeros() + #[inline] + pub fn max_mip_levels(&self) -> u32 { + // This calculates `log2(max(width, height, depth)) + 1` using fast integer operations. + let max = match *self { + ImageDimensions::Dim1d { width, .. } => width, + ImageDimensions::Dim2d { width, height, .. } => width | height, + ImageDimensions::Dim3d { + width, + height, + depth, + } => width | height | depth, + }; + 32 - max.leading_zeros() } /// Returns the dimensions of the `level`th mipmap level. If `level` is 0, then the dimensions /// are left unchanged. /// - /// Returns `None` if `level` is superior or equal to `max_mipmaps()`. + /// Returns `None` if `level` is superior or equal to `max_mip_levels()`. /// - /// # Example + /// # Examples /// /// ``` /// use vulkano::image::ImageDimensions; @@ -475,36 +506,36 @@ impl ImageDimensions { /// array_layers: 1, /// }; /// - /// assert_eq!(dims.mipmap_dimensions(0), Some(dims)); - /// assert_eq!(dims.mipmap_dimensions(1), Some(ImageDimensions::Dim2d { + /// assert_eq!(dims.mip_level_dimensions(0), Some(dims)); + /// assert_eq!(dims.mip_level_dimensions(1), Some(ImageDimensions::Dim2d { /// width: 481, /// height: 128, /// array_layers: 1, /// })); - /// assert_eq!(dims.mipmap_dimensions(6), Some(ImageDimensions::Dim2d { + /// assert_eq!(dims.mip_level_dimensions(6), Some(ImageDimensions::Dim2d { /// width: 15, /// height: 4, /// array_layers: 1, /// })); - /// assert_eq!(dims.mipmap_dimensions(9), Some(ImageDimensions::Dim2d { + /// assert_eq!(dims.mip_level_dimensions(9), Some(ImageDimensions::Dim2d { /// width: 1, /// height: 1, /// array_layers: 1, /// })); - /// assert_eq!(dims.mipmap_dimensions(11), None); + /// assert_eq!(dims.mip_level_dimensions(11), None); /// ``` /// - /// # Panic - /// - /// In debug mode, Panics if `width`, `height` or `depth` is equal to 0. In release, returns - /// an unspecified value. + /// # Panics /// - pub fn mipmap_dimensions(&self, level: u32) -> Option<ImageDimensions> { + /// - In debug mode, panics if `width`, `height` or `depth` is equal to 0. In release, returns + /// an unspecified value. + #[inline] + pub fn mip_level_dimensions(&self, level: u32) -> Option<ImageDimensions> { if level == 0 { return Some(*self); } - if level >= self.max_mipmaps() { + if level >= self.max_mip_levels() { return None; } @@ -551,47 +582,436 @@ impl ImageDimensions { } } +/// One or more subresources of an image, spanning a single mip level, that should be accessed by a +/// command. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ImageSubresourceLayers { + /// Selects the aspects that will be included. + /// + /// The value must not be empty, and must not include any of the `memory_plane` aspects. + /// The `color` aspect cannot be selected together any of with the `plane` aspects. + pub aspects: ImageAspects, + + /// Selects mip level that will be included. + pub mip_level: u32, + + /// Selects the range of array layers that will be included. + /// + /// The range must not be empty. + pub array_layers: Range<u32>, +} + +impl ImageSubresourceLayers { + /// Returns an `ImageSubresourceLayers` from the given image parameters, covering the first + /// mip level of the image. All aspects of the image are selected, or `plane0` if the image + /// is multi-planar. + #[inline] + pub fn from_parameters(format: Format, array_layers: u32) -> Self { + Self { + aspects: { + let aspects = format.aspects(); + + if aspects.intersects(ImageAspects::PLANE_0) { + ImageAspects::PLANE_0 + } else { + aspects + } + }, + mip_level: 0, + array_layers: 0..array_layers, + } + } +} + +impl From<ImageSubresourceLayers> for ash::vk::ImageSubresourceLayers { + #[inline] + fn from(val: ImageSubresourceLayers) -> Self { + Self { + aspect_mask: val.aspects.into(), + mip_level: val.mip_level, + base_array_layer: val.array_layers.start, + layer_count: val.array_layers.end - val.array_layers.start, + } + } +} + +/// One or more subresources of an image that should be accessed by a command. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ImageSubresourceRange { + /// Selects the aspects that will be included. + /// + /// The value must not be empty, and must not include any of the `memory_plane` aspects. + /// The `color` aspect cannot be selected together any of with the `plane` aspects. + pub aspects: ImageAspects, + + /// Selects the range of the mip levels that will be included. + /// + /// The range must not be empty. + pub mip_levels: Range<u32>, + + /// Selects the range of array layers that will be included. + /// + /// The range must not be empty. + pub array_layers: Range<u32>, +} + +impl ImageSubresourceRange { + /// Returns an `ImageSubresourceRange` from the given image parameters, covering the whole + /// image. If the image is multi-planar, only the `color` aspect is selected. + #[inline] + pub fn from_parameters(format: Format, mip_levels: u32, array_layers: u32) -> Self { + Self { + aspects: format.aspects() + - (ImageAspects::PLANE_0 | ImageAspects::PLANE_1 | ImageAspects::PLANE_2), + mip_levels: 0..mip_levels, + array_layers: 0..array_layers, + } + } +} + +impl From<ImageSubresourceRange> for ash::vk::ImageSubresourceRange { + #[inline] + fn from(val: ImageSubresourceRange) -> Self { + Self { + aspect_mask: val.aspects.into(), + base_mip_level: val.mip_levels.start, + level_count: val.mip_levels.end - val.mip_levels.start, + base_array_layer: val.array_layers.start, + layer_count: val.array_layers.end - val.array_layers.start, + } + } +} + +impl From<ImageSubresourceLayers> for ImageSubresourceRange { + #[inline] + fn from(val: ImageSubresourceLayers) -> Self { + Self { + aspects: val.aspects, + mip_levels: val.mip_level..val.mip_level + 1, + array_layers: val.array_layers, + } + } +} + +/// Describes the memory layout of an image. +/// +/// The address of a texel at `(x, y, z, layer)` is `layer * array_pitch + z * depth_pitch + +/// y * row_pitch + x * size_of_each_texel + offset`. `size_of_each_texel` must be determined +/// depending on the format. The same formula applies for compressed formats, except that the +/// coordinates must be in number of blocks. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SubresourceLayout { + /// The number of bytes from the start of the memory where the subresource begins. + pub offset: DeviceSize, + + /// The size in bytes in the subresource. It includes any extra memory that is required based on + /// `row_pitch`. + pub size: DeviceSize, + + /// The number of bytes between adjacent rows of texels. + pub row_pitch: DeviceSize, + + /// The number of bytes between adjacent array layers. + /// + /// This value is undefined for images with only one array layer. + pub array_pitch: DeviceSize, + + /// The number of bytes between adjacent depth slices. + /// + /// This value is undefined for images that are not three-dimensional. + pub depth_pitch: DeviceSize, +} + +/// The image configuration to query in +/// [`PhysicalDevice::image_format_properties`](crate::device::physical::PhysicalDevice::image_format_properties). +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ImageFormatInfo { + /// The `flags` that the image will have. + /// + /// The default value is [`ImageCreateFlags::empty()`]. + pub flags: ImageCreateFlags, + + /// The `format` that the image will have. + /// + /// The default value is `None`, which must be overridden. + pub format: Option<Format>, + + /// The dimension type that the image will have. + /// + /// The default value is [`ImageType::Dim2d`]. + pub image_type: ImageType, + + /// The `tiling` that the image will have. + /// + /// The default value is [`ImageTiling::Optimal`]. + pub tiling: ImageTiling, + + /// The `usage` that the image will have. + /// + /// The default value is [`ImageUsage::empty()`], which must be overridden. + pub usage: ImageUsage, + + /// The `stencil_usage` that the image will have. + /// + /// If `stencil_usage` is empty or if `format` does not have both a depth and a stencil aspect, + /// then it is automatically set to equal `usage`. + /// + /// If after this, `stencil_usage` does not equal `usage`, + /// then the physical device API version must be at least 1.2, or the + /// [`ext_separate_stencil_usage`](crate::device::DeviceExtensions::ext_separate_stencil_usage) + /// extension must be supported by the physical device. + /// + /// The default value is [`ImageUsage::empty()`]. + pub stencil_usage: ImageUsage, + + /// An external memory handle type that will be imported to or exported from the image. + /// + /// This is needed to retrieve the + /// [`external_memory_properties`](ImageFormatProperties::external_memory_properties) value, + /// and the physical device API version must be at least 1.1 or the + /// [`khr_external_memory_capabilities`](crate::instance::InstanceExtensions::khr_external_memory_capabilities) + /// extension must be enabled on the instance. + /// + /// The default value is `None`. + pub external_memory_handle_type: Option<ExternalMemoryHandleType>, + + /// The image view type that will be created from the image. + /// + /// This is needed to retrieve the + /// [`filter_cubic`](ImageFormatProperties::filter_cubic) and + /// [`filter_cubic_minmax`](ImageFormatProperties::filter_cubic_minmax) values, and the + /// [`ext_filter_cubic`](crate::device::DeviceExtensions::ext_filter_cubic) extension must be + /// supported on the physical device. + /// + /// The default value is `None`. + pub image_view_type: Option<ImageViewType>, + + pub _ne: crate::NonExhaustive, +} + +impl Default for ImageFormatInfo { + #[inline] + fn default() -> Self { + Self { + flags: ImageCreateFlags::empty(), + format: None, + image_type: ImageType::Dim2d, + tiling: ImageTiling::Optimal, + usage: ImageUsage::empty(), + stencil_usage: ImageUsage::empty(), + external_memory_handle_type: None, + image_view_type: None, + _ne: crate::NonExhaustive(()), + } + } +} + +/// The properties that are supported by a physical device for images of a certain type. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub struct ImageFormatProperties { + /// The maximum dimensions. + pub max_extent: [u32; 3], + + /// The maximum number of mipmap levels. + pub max_mip_levels: u32, + + /// The maximum number of array layers. + pub max_array_layers: u32, + + /// The supported sample counts. + pub sample_counts: SampleCounts, + + /// The maximum total size of an image, in bytes. This is guaranteed to be at least + /// 0x80000000. + pub max_resource_size: DeviceSize, + + /// The properties for external memory. + /// This will be [`ExternalMemoryProperties::default()`] if `external_handle_type` was `None`. + pub external_memory_properties: ExternalMemoryProperties, + + /// When querying with an image view type, whether such image views support sampling with + /// a [`Cubic`](crate::sampler::Filter::Cubic) `mag_filter` or `min_filter`. + pub filter_cubic: bool, + + /// When querying with an image view type, whether such image views support sampling with + /// a [`Cubic`](crate::sampler::Filter::Cubic) `mag_filter` or `min_filter`, and with a + /// [`Min`](crate::sampler::SamplerReductionMode::Min) or + /// [`Max`](crate::sampler::SamplerReductionMode::Max) `reduction_mode`. + pub filter_cubic_minmax: bool, +} + +impl From<ash::vk::ImageFormatProperties> for ImageFormatProperties { + #[inline] + fn from(props: ash::vk::ImageFormatProperties) -> Self { + Self { + max_extent: [ + props.max_extent.width, + props.max_extent.height, + props.max_extent.depth, + ], + max_mip_levels: props.max_mip_levels, + max_array_layers: props.max_array_layers, + sample_counts: props.sample_counts.into(), + max_resource_size: props.max_resource_size, + external_memory_properties: Default::default(), + filter_cubic: false, + filter_cubic_minmax: false, + } + } +} + +/// The image configuration to query in +/// [`PhysicalDevice::sparse_image_format_properties`](crate::device::physical::PhysicalDevice::sparse_image_format_properties). +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct SparseImageFormatInfo { + /// The `format` that the image will have. + /// + /// The default value is `None`, which must be overridden. + pub format: Option<Format>, + + /// The dimension type that the image will have. + /// + /// The default value is [`ImageType::Dim2d`]. + pub image_type: ImageType, + + /// The `samples` that the image will have. + /// + /// The default value is `SampleCount::Sample1`. + pub samples: SampleCount, + + /// The `usage` that the image will have. + /// + /// The default value is [`ImageUsage::empty()`], which must be overridden. + pub usage: ImageUsage, + + /// The `tiling` that the image will have. + /// + /// The default value is [`ImageTiling::Optimal`]. + pub tiling: ImageTiling, + + pub _ne: crate::NonExhaustive, +} + +impl Default for SparseImageFormatInfo { + #[inline] + fn default() -> Self { + Self { + format: None, + image_type: ImageType::Dim2d, + samples: SampleCount::Sample1, + usage: ImageUsage::empty(), + tiling: ImageTiling::Optimal, + _ne: crate::NonExhaustive(()), + } + } +} + +/// The properties that are supported by a physical device for sparse images of a certain type. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub struct SparseImageFormatProperties { + /// The aspects of the image that the properties apply to. + pub aspects: ImageAspects, + + /// The size of the sparse image block, in texels or compressed texel blocks. + /// + /// If `flags.nonstandard_block_size` is set, then these values do not match the standard + /// sparse block dimensions for the given format. + pub image_granularity: [u32; 3], + + /// Additional information about the sparse image. + pub flags: SparseImageFormatFlags, +} + +vulkan_bitflags! { + #[non_exhaustive] + + /// Flags specifying information about a sparse resource. + SparseImageFormatFlags = SparseImageFormatFlags(u32); + + /// The image uses a single mip tail region for all array layers, instead of one mip tail region + /// per array layer. + SINGLE_MIPTAIL = SINGLE_MIPTAIL, + + /// The image's mip tail region begins with the first mip level whose dimensions are not an + /// integer multiple of the corresponding sparse image block dimensions. + ALIGNED_MIP_SIZE = ALIGNED_MIP_SIZE, + + /// The image uses non-standard sparse image block dimensions. + NONSTANDARD_BLOCK_SIZE = NONSTANDARD_BLOCK_SIZE, +} + +/// Requirements for binding memory to a sparse image. +#[derive(Clone, Debug)] +#[non_exhaustive] +pub struct SparseImageMemoryRequirements { + /// The properties of the image format. + pub format_properties: SparseImageFormatProperties, + + /// The first mip level at which image subresources are included in the mip tail region. + pub image_mip_tail_first_lod: u32, + + /// The size in bytes of the mip tail region. This value is guaranteed to be a multiple of the + /// sparse block size in bytes. + /// + /// If `format_properties.flags.single_miptail` is set, then this is the size of the whole + /// mip tail. Otherwise it is the size of the mip tail of a single array layer. + pub image_mip_tail_size: DeviceSize, + + /// The memory offset that must be used to bind the mip tail region. + pub image_mip_tail_offset: DeviceSize, + + /// If `format_properties.flags.single_miptail` is not set, specifies the stride between + /// the mip tail regions of each array layer. + pub image_mip_tail_stride: Option<DeviceSize>, +} + #[cfg(test)] mod tests { - use crate::format::Format; - use crate::image::ImageDimensions; - use crate::image::ImmutableImage; - use crate::image::MipmapsCount; + use crate::{ + command_buffer::{ + allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, + }, + format::Format, + image::{ImageAccess, ImageDimensions, ImmutableImage, MipmapsCount}, + memory::allocator::StandardMemoryAllocator, + }; #[test] - fn max_mipmaps() { + fn max_mip_levels() { let dims = ImageDimensions::Dim2d { width: 2, height: 1, array_layers: 1, }; - assert_eq!(dims.max_mipmaps(), 2); + assert_eq!(dims.max_mip_levels(), 2); let dims = ImageDimensions::Dim2d { width: 2, height: 3, array_layers: 1, }; - assert_eq!(dims.max_mipmaps(), 2); + assert_eq!(dims.max_mip_levels(), 2); let dims = ImageDimensions::Dim2d { width: 512, height: 512, array_layers: 1, }; - assert_eq!(dims.max_mipmaps(), 10); + assert_eq!(dims.max_mip_levels(), 10); } #[test] - fn mipmap_dimensions() { + fn mip_level_dimensions() { let dims = ImageDimensions::Dim2d { width: 283, height: 175, array_layers: 1, }; - assert_eq!(dims.mipmap_dimensions(0), Some(dims)); + assert_eq!(dims.mip_level_dimensions(0), Some(dims)); assert_eq!( - dims.mipmap_dimensions(1), + dims.mip_level_dimensions(1), Some(ImageDimensions::Dim2d { width: 141, height: 87, @@ -599,7 +1019,7 @@ mod tests { }) ); assert_eq!( - dims.mipmap_dimensions(2), + dims.mip_level_dimensions(2), Some(ImageDimensions::Dim2d { width: 70, height: 43, @@ -607,7 +1027,7 @@ mod tests { }) ); assert_eq!( - dims.mipmap_dimensions(3), + dims.mip_level_dimensions(3), Some(ImageDimensions::Dim2d { width: 35, height: 21, @@ -616,7 +1036,7 @@ mod tests { ); assert_eq!( - dims.mipmap_dimensions(4), + dims.mip_level_dimensions(4), Some(ImageDimensions::Dim2d { width: 17, height: 10, @@ -624,7 +1044,7 @@ mod tests { }) ); assert_eq!( - dims.mipmap_dimensions(5), + dims.mip_level_dimensions(5), Some(ImageDimensions::Dim2d { width: 8, height: 5, @@ -632,7 +1052,7 @@ mod tests { }) ); assert_eq!( - dims.mipmap_dimensions(6), + dims.mip_level_dimensions(6), Some(ImageDimensions::Dim2d { width: 4, height: 2, @@ -640,7 +1060,7 @@ mod tests { }) ); assert_eq!( - dims.mipmap_dimensions(7), + dims.mip_level_dimensions(7), Some(ImageDimensions::Dim2d { width: 2, height: 1, @@ -648,20 +1068,29 @@ mod tests { }) ); assert_eq!( - dims.mipmap_dimensions(8), + dims.mip_level_dimensions(8), Some(ImageDimensions::Dim2d { width: 1, height: 1, array_layers: 1, }) ); - assert_eq!(dims.mipmap_dimensions(9), None); + assert_eq!(dims.mip_level_dimensions(9), None); } #[test] fn mipmap_working_immutable_image() { let (device, queue) = gfx_dev_and_queue!(); + let cb_allocator = StandardCommandBufferAllocator::new(device.clone(), Default::default()); + let mut cbb = AutoCommandBufferBuilder::primary( + &cb_allocator, + queue.queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap(); + + let memory_allocator = StandardMemoryAllocator::new_default(device); let dimensions = ImageDimensions::Dim2d { width: 512, height: 512, @@ -672,30 +1101,32 @@ mod tests { vec.resize(512 * 512, 0u8); - let (image, _) = ImmutableImage::from_iter( + let image = ImmutableImage::from_iter( + &memory_allocator, vec.into_iter(), dimensions, MipmapsCount::One, - Format::R8Unorm, - queue.clone(), + Format::R8_UNORM, + &mut cbb, ) .unwrap(); - assert_eq!(image.mipmap_levels(), 1); + assert_eq!(image.mip_levels(), 1); } { let mut vec = Vec::new(); vec.resize(512 * 512, 0u8); - let (image, _) = ImmutableImage::from_iter( + let image = ImmutableImage::from_iter( + &memory_allocator, vec.into_iter(), dimensions, MipmapsCount::Log2, - Format::R8Unorm, - queue.clone(), + Format::R8_UNORM, + &mut cbb, ) .unwrap(); - assert_eq!(image.mipmap_levels(), 10); + assert_eq!(image.mip_levels(), 10); } } } |