aboutsummaryrefslogtreecommitdiff
path: root/src/image/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/image/mod.rs')
-rw-r--r--src/image/mod.rs1011
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);
}
}
}