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.rs701
1 files changed, 701 insertions, 0 deletions
diff --git a/src/image/mod.rs b/src/image/mod.rs
new file mode 100644
index 0000000..4e7f4af
--- /dev/null
+++ b/src/image/mod.rs
@@ -0,0 +1,701 @@
+// Copyright (c) 2016 The vulkano developers
+// Licensed under the Apache License, Version 2.0
+// <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT
+// license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
+// at your option. All files in the project carrying such
+// notice may not be copied, modified, or distributed except
+// according to those terms.
+
+//! Image storage (1D, 2D, 3D, arrays, etc.) and image views.
+//!
+//! An *image* is a region of memory whose purpose is to store multi-dimensional data. Its
+//! most common use is to store a 2D array of color pixels (in other words an *image* in
+//! everyday language), but it can also be used to store arbitrary data.
+//!
+//! The advantage of using an image compared to a buffer is that the memory layout is optimized
+//! for locality. When reading a specific pixel of an image, reading the nearby pixels is really
+//! fast. Most implementations have hardware dedicated to reading from images if you access them
+//! through a sampler.
+//!
+//! # Properties of an image
+//!
+//! # Images and image views
+//!
+//! There is a distinction between *images* and *image views*. As its name suggests, an image
+//! view describes how the GPU must interpret the image.
+//!
+//! Transfer and memory operations operate on images themselves, while reading/writing an image
+//! operates on image views. You can create multiple image views from the same image.
+//!
+//! # High-level wrappers
+//!
+//! In the vulkano library, an image is any object that implements the [`ImageAccess`] trait. You
+//! can create a view by wrapping them in an [`ImageView`](crate::image::view::ImageView).
+//!
+//! Since the `ImageAccess` trait is low-level, you are encouraged to not implement it yourself but
+//! instead use one of the provided implementations that are specialized depending on the way you
+//! are going to use the image:
+//!
+//! - An `AttachmentImage` can be used when you want to draw to an image.
+//! - An `ImmutableImage` stores data which never need be changed after the initial upload,
+//! like a texture.
+//!
+//! # Low-level information
+//!
+//! 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;
+
+mod aspect;
+pub mod attachment; // TODO: make private
+pub mod immutable; // TODO: make private
+mod layout;
+mod storage;
+pub mod swapchain; // TODO: make private
+pub mod sys;
+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(),
+}
+
+impl From<SampleCount> for ash::vk::SampleCountFlags {
+ #[inline]
+ fn from(val: SampleCount) -> Self {
+ Self::from_raw(val as u32)
+ }
+}
+
+impl TryFrom<ash::vk::SampleCountFlags> for SampleCount {
+ type Error = ();
+
+ #[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(()),
+ }
+ }
+}
+
+impl TryFrom<u32> for SampleCount {
+ type Error = ();
+
+ #[inline]
+ fn try_from(val: u32) -> Result<Self, Self::Error> {
+ match val {
+ 1 => Ok(Self::Sample1),
+ 2 => Ok(Self::Sample2),
+ 4 => Ok(Self::Sample4),
+ 8 => Ok(Self::Sample8),
+ 16 => Ok(Self::Sample16),
+ 32 => Ok(Self::Sample32),
+ 64 => Ok(Self::Sample64),
+ _ => Err(()),
+ }
+ }
+}
+
+/// 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)]
+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.
+ ///
+ /// Note that this is not necessarily the maximum number of mipmaps, as the Vulkan
+ /// implementation may report that it supports a greater value.
+ Log2,
+
+ /// Allocate one mipmap (ie. just the main level). Always supported.
+ One,
+
+ /// Allocate the given number of mipmaps. May result in an error if the value is out of range
+ /// of what the implementation supports.
+ Specific(u32),
+}
+
+impl From<u32> for MipmapsCount {
+ #[inline]
+ fn from(num: u32) -> MipmapsCount {
+ MipmapsCount::Specific(num)
+ }
+}
+
+/// Helper type for creating extents
+#[derive(Debug, Copy, Clone)]
+pub enum Extent {
+ E1D([u32; 1]),
+ E2D([u32; 2]),
+ E3D([u32; 3]),
+}
+
+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 = ();
+
+ 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(()),
+ }
+ }
+}
+
+impl TryFrom<Extent> for ash::vk::Extent3D {
+ type Error = ();
+
+ 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(()),
+ }
+ }
+}
+
+/// 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,
+}
+
+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,
+ }
+ }
+}
+
+#[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,
+}
+
+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,
+ }
+ }
+
+ 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)
+ }
+}
+
+/// The dimensions of an image.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum ImageDimensions {
+ Dim1d {
+ width: u32,
+ array_layers: u32,
+ },
+ Dim2d {
+ width: u32,
+ height: u32,
+ array_layers: u32,
+ },
+ Dim3d {
+ width: u32,
+ height: u32,
+ depth: u32,
+ },
+}
+
+impl ImageDimensions {
+ #[inline]
+ pub fn width(&self) -> u32 {
+ match *self {
+ ImageDimensions::Dim1d { width, .. } => width,
+ ImageDimensions::Dim2d { width, .. } => width,
+ ImageDimensions::Dim3d { width, .. } => width,
+ }
+ }
+
+ #[inline]
+ pub fn height(&self) -> u32 {
+ match *self {
+ ImageDimensions::Dim1d { .. } => 1,
+ ImageDimensions::Dim2d { height, .. } => height,
+ ImageDimensions::Dim3d { height, .. } => height,
+ }
+ }
+
+ #[inline]
+ pub fn width_height(&self) -> [u32; 2] {
+ [self.width(), self.height()]
+ }
+
+ #[inline]
+ pub fn depth(&self) -> u32 {
+ match *self {
+ ImageDimensions::Dim1d { .. } => 1,
+ ImageDimensions::Dim2d { .. } => 1,
+ ImageDimensions::Dim3d { depth, .. } => depth,
+ }
+ }
+
+ #[inline]
+ pub fn width_height_depth(&self) -> [u32; 3] {
+ [self.width(), self.height(), self.depth()]
+ }
+
+ #[inline]
+ pub fn array_layers(&self) -> u32 {
+ match *self {
+ ImageDimensions::Dim1d { array_layers, .. } => array_layers,
+ ImageDimensions::Dim2d { array_layers, .. } => array_layers,
+ ImageDimensions::Dim3d { .. } => 1,
+ }
+ }
+
+ /// Returns the total number of texels for an image of these dimensions.
+ #[inline]
+ pub fn num_texels(&self) -> u32 {
+ self.width() * self.height() * self.depth() * self.array_layers()
+ }
+
+ /// Returns the maximum number of mipmaps for these image dimensions.
+ ///
+ /// The returned value is always at least superior or equal to 1.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use vulkano::image::ImageDimensions;
+ ///
+ /// let dims = ImageDimensions::Dim2d {
+ /// width: 32,
+ /// height: 50,
+ /// array_layers: 1,
+ /// };
+ ///
+ /// assert_eq!(dims.max_mipmaps(), 6);
+ /// ```
+ ///
+ pub fn max_mipmaps(&self) -> u32 {
+ 32 - (self.width() | self.height() | self.depth()).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()`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use vulkano::image::ImageDimensions;
+ ///
+ /// let dims = ImageDimensions::Dim2d {
+ /// width: 963,
+ /// height: 256,
+ /// array_layers: 1,
+ /// };
+ ///
+ /// assert_eq!(dims.mipmap_dimensions(0), Some(dims));
+ /// assert_eq!(dims.mipmap_dimensions(1), Some(ImageDimensions::Dim2d {
+ /// width: 481,
+ /// height: 128,
+ /// array_layers: 1,
+ /// }));
+ /// assert_eq!(dims.mipmap_dimensions(6), Some(ImageDimensions::Dim2d {
+ /// width: 15,
+ /// height: 4,
+ /// array_layers: 1,
+ /// }));
+ /// assert_eq!(dims.mipmap_dimensions(9), Some(ImageDimensions::Dim2d {
+ /// width: 1,
+ /// height: 1,
+ /// array_layers: 1,
+ /// }));
+ /// assert_eq!(dims.mipmap_dimensions(11), None);
+ /// ```
+ ///
+ /// # Panic
+ ///
+ /// In debug mode, Panics if `width`, `height` or `depth` is equal to 0. In release, returns
+ /// an unspecified value.
+ ///
+ pub fn mipmap_dimensions(&self, level: u32) -> Option<ImageDimensions> {
+ if level == 0 {
+ return Some(*self);
+ }
+
+ if level >= self.max_mipmaps() {
+ return None;
+ }
+
+ Some(match *self {
+ ImageDimensions::Dim1d {
+ width,
+ array_layers,
+ } => {
+ debug_assert_ne!(width, 0);
+ ImageDimensions::Dim1d {
+ array_layers,
+ width: cmp::max(1, width >> level),
+ }
+ }
+
+ ImageDimensions::Dim2d {
+ width,
+ height,
+ array_layers,
+ } => {
+ debug_assert_ne!(width, 0);
+ debug_assert_ne!(height, 0);
+ ImageDimensions::Dim2d {
+ width: cmp::max(1, width >> level),
+ height: cmp::max(1, height >> level),
+ array_layers,
+ }
+ }
+
+ ImageDimensions::Dim3d {
+ width,
+ height,
+ depth,
+ } => {
+ debug_assert_ne!(width, 0);
+ debug_assert_ne!(height, 0);
+ ImageDimensions::Dim3d {
+ width: cmp::max(1, width >> level),
+ height: cmp::max(1, height >> level),
+ depth: cmp::max(1, depth >> level),
+ }
+ }
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::format::Format;
+ use crate::image::ImageDimensions;
+ use crate::image::ImmutableImage;
+ use crate::image::MipmapsCount;
+
+ #[test]
+ fn max_mipmaps() {
+ let dims = ImageDimensions::Dim2d {
+ width: 2,
+ height: 1,
+ array_layers: 1,
+ };
+ assert_eq!(dims.max_mipmaps(), 2);
+
+ let dims = ImageDimensions::Dim2d {
+ width: 2,
+ height: 3,
+ array_layers: 1,
+ };
+ assert_eq!(dims.max_mipmaps(), 2);
+
+ let dims = ImageDimensions::Dim2d {
+ width: 512,
+ height: 512,
+ array_layers: 1,
+ };
+ assert_eq!(dims.max_mipmaps(), 10);
+ }
+
+ #[test]
+ fn mipmap_dimensions() {
+ let dims = ImageDimensions::Dim2d {
+ width: 283,
+ height: 175,
+ array_layers: 1,
+ };
+ assert_eq!(dims.mipmap_dimensions(0), Some(dims));
+ assert_eq!(
+ dims.mipmap_dimensions(1),
+ Some(ImageDimensions::Dim2d {
+ width: 141,
+ height: 87,
+ array_layers: 1,
+ })
+ );
+ assert_eq!(
+ dims.mipmap_dimensions(2),
+ Some(ImageDimensions::Dim2d {
+ width: 70,
+ height: 43,
+ array_layers: 1,
+ })
+ );
+ assert_eq!(
+ dims.mipmap_dimensions(3),
+ Some(ImageDimensions::Dim2d {
+ width: 35,
+ height: 21,
+ array_layers: 1,
+ })
+ );
+
+ assert_eq!(
+ dims.mipmap_dimensions(4),
+ Some(ImageDimensions::Dim2d {
+ width: 17,
+ height: 10,
+ array_layers: 1,
+ })
+ );
+ assert_eq!(
+ dims.mipmap_dimensions(5),
+ Some(ImageDimensions::Dim2d {
+ width: 8,
+ height: 5,
+ array_layers: 1,
+ })
+ );
+ assert_eq!(
+ dims.mipmap_dimensions(6),
+ Some(ImageDimensions::Dim2d {
+ width: 4,
+ height: 2,
+ array_layers: 1,
+ })
+ );
+ assert_eq!(
+ dims.mipmap_dimensions(7),
+ Some(ImageDimensions::Dim2d {
+ width: 2,
+ height: 1,
+ array_layers: 1,
+ })
+ );
+ assert_eq!(
+ dims.mipmap_dimensions(8),
+ Some(ImageDimensions::Dim2d {
+ width: 1,
+ height: 1,
+ array_layers: 1,
+ })
+ );
+ assert_eq!(dims.mipmap_dimensions(9), None);
+ }
+
+ #[test]
+ fn mipmap_working_immutable_image() {
+ let (device, queue) = gfx_dev_and_queue!();
+
+ let dimensions = ImageDimensions::Dim2d {
+ width: 512,
+ height: 512,
+ array_layers: 1,
+ };
+ {
+ let mut vec = Vec::new();
+
+ vec.resize(512 * 512, 0u8);
+
+ let (image, _) = ImmutableImage::from_iter(
+ vec.into_iter(),
+ dimensions,
+ MipmapsCount::One,
+ Format::R8Unorm,
+ queue.clone(),
+ )
+ .unwrap();
+ assert_eq!(image.mipmap_levels(), 1);
+ }
+ {
+ let mut vec = Vec::new();
+
+ vec.resize(512 * 512, 0u8);
+
+ let (image, _) = ImmutableImage::from_iter(
+ vec.into_iter(),
+ dimensions,
+ MipmapsCount::Log2,
+ Format::R8Unorm,
+ queue.clone(),
+ )
+ .unwrap();
+ assert_eq!(image.mipmap_levels(), 10);
+ }
+ }
+}