| // 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, 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 |
| 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; |
| |
| vulkan_bitflags! { |
| #[non_exhaustive] |
| |
| /// 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], |
| },*/ |
| } |
| |
| vulkan_bitflags_enum! { |
| #[non_exhaustive] |
| |
| /// 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 |
| } |
| } |
| |
| 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 mipmaps must be allocated. |
| /// |
| /// Note that at least one mipmap must be allocated, to store the main level of the image. |
| #[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. |
| /// |
| /// 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) |
| } |
| } |
| |
| vulkan_enum! { |
| #[non_exhaustive] |
| |
| // TODO: document |
| ImageType = ImageType(i32); |
| |
| // TODO: document |
| Dim1d = TYPE_1D, |
| |
| // TODO: document |
| Dim2d = TYPE_2D, |
| |
| // TODO: document |
| Dim3d = TYPE_3D, |
| } |
| |
| vulkan_enum! { |
| #[non_exhaustive] |
| |
| // TODO: document |
| ImageTiling = ImageTiling(i32); |
| |
| // TODO: document |
| Optimal = OPTIMAL, |
| |
| // TODO: document |
| Linear = LINEAR, |
| |
| // TODO: document |
| DrmFormatModifier = DRM_FORMAT_MODIFIER_EXT { |
| device_extensions: [ext_image_drm_format_modifier], |
| }, |
| } |
| |
| /// 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() |
| } |
| |
| #[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 1. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use vulkano::image::ImageDimensions; |
| /// |
| /// let dims = ImageDimensions::Dim2d { |
| /// width: 32, |
| /// height: 50, |
| /// array_layers: 1, |
| /// }; |
| /// |
| /// assert_eq!(dims.max_mip_levels(), 6); |
| /// ``` |
| #[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_mip_levels()`. |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// use vulkano::image::ImageDimensions; |
| /// |
| /// let dims = ImageDimensions::Dim2d { |
| /// width: 963, |
| /// height: 256, |
| /// array_layers: 1, |
| /// }; |
| /// |
| /// 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.mip_level_dimensions(6), Some(ImageDimensions::Dim2d { |
| /// width: 15, |
| /// height: 4, |
| /// array_layers: 1, |
| /// })); |
| /// assert_eq!(dims.mip_level_dimensions(9), Some(ImageDimensions::Dim2d { |
| /// width: 1, |
| /// height: 1, |
| /// array_layers: 1, |
| /// })); |
| /// assert_eq!(dims.mip_level_dimensions(11), None); |
| /// ``` |
| /// |
| /// # Panics |
| /// |
| /// - 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_mip_levels() { |
| 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), |
| } |
| } |
| }) |
| } |
| } |
| |
| /// 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::{ |
| command_buffer::{ |
| allocator::StandardCommandBufferAllocator, AutoCommandBufferBuilder, CommandBufferUsage, |
| }, |
| format::Format, |
| image::{ImageAccess, ImageDimensions, ImmutableImage, MipmapsCount}, |
| memory::allocator::StandardMemoryAllocator, |
| }; |
| |
| #[test] |
| fn max_mip_levels() { |
| let dims = ImageDimensions::Dim2d { |
| width: 2, |
| height: 1, |
| array_layers: 1, |
| }; |
| assert_eq!(dims.max_mip_levels(), 2); |
| |
| let dims = ImageDimensions::Dim2d { |
| width: 2, |
| height: 3, |
| array_layers: 1, |
| }; |
| assert_eq!(dims.max_mip_levels(), 2); |
| |
| let dims = ImageDimensions::Dim2d { |
| width: 512, |
| height: 512, |
| array_layers: 1, |
| }; |
| assert_eq!(dims.max_mip_levels(), 10); |
| } |
| |
| #[test] |
| fn mip_level_dimensions() { |
| let dims = ImageDimensions::Dim2d { |
| width: 283, |
| height: 175, |
| array_layers: 1, |
| }; |
| assert_eq!(dims.mip_level_dimensions(0), Some(dims)); |
| assert_eq!( |
| dims.mip_level_dimensions(1), |
| Some(ImageDimensions::Dim2d { |
| width: 141, |
| height: 87, |
| array_layers: 1, |
| }) |
| ); |
| assert_eq!( |
| dims.mip_level_dimensions(2), |
| Some(ImageDimensions::Dim2d { |
| width: 70, |
| height: 43, |
| array_layers: 1, |
| }) |
| ); |
| assert_eq!( |
| dims.mip_level_dimensions(3), |
| Some(ImageDimensions::Dim2d { |
| width: 35, |
| height: 21, |
| array_layers: 1, |
| }) |
| ); |
| |
| assert_eq!( |
| dims.mip_level_dimensions(4), |
| Some(ImageDimensions::Dim2d { |
| width: 17, |
| height: 10, |
| array_layers: 1, |
| }) |
| ); |
| assert_eq!( |
| dims.mip_level_dimensions(5), |
| Some(ImageDimensions::Dim2d { |
| width: 8, |
| height: 5, |
| array_layers: 1, |
| }) |
| ); |
| assert_eq!( |
| dims.mip_level_dimensions(6), |
| Some(ImageDimensions::Dim2d { |
| width: 4, |
| height: 2, |
| array_layers: 1, |
| }) |
| ); |
| assert_eq!( |
| dims.mip_level_dimensions(7), |
| Some(ImageDimensions::Dim2d { |
| width: 2, |
| height: 1, |
| array_layers: 1, |
| }) |
| ); |
| assert_eq!( |
| dims.mip_level_dimensions(8), |
| Some(ImageDimensions::Dim2d { |
| width: 1, |
| height: 1, |
| array_layers: 1, |
| }) |
| ); |
| 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, |
| array_layers: 1, |
| }; |
| { |
| let mut vec = Vec::new(); |
| |
| vec.resize(512 * 512, 0u8); |
| |
| let image = ImmutableImage::from_iter( |
| &memory_allocator, |
| vec.into_iter(), |
| dimensions, |
| MipmapsCount::One, |
| Format::R8_UNORM, |
| &mut cbb, |
| ) |
| .unwrap(); |
| assert_eq!(image.mip_levels(), 1); |
| } |
| { |
| let mut vec = Vec::new(); |
| |
| vec.resize(512 * 512, 0u8); |
| |
| let image = ImmutableImage::from_iter( |
| &memory_allocator, |
| vec.into_iter(), |
| dimensions, |
| MipmapsCount::Log2, |
| Format::R8_UNORM, |
| &mut cbb, |
| ) |
| .unwrap(); |
| assert_eq!(image.mip_levels(), 10); |
| } |
| } |
| } |