aboutsummaryrefslogtreecommitdiff
path: root/src/image/view.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/image/view.rs')
-rw-r--r--src/image/view.rs1665
1 files changed, 1221 insertions, 444 deletions
diff --git a/src/image/view.rs b/src/image/view.rs
index 894a343..99bed3b 100644
--- a/src/image/view.rs
+++ b/src/image/view.rs
@@ -13,609 +13,1386 @@
//! an image and describes how the GPU should interpret the data. It is needed when an image is
//! to be used in a shader descriptor or as a framebuffer attachment.
-use crate::check_errors;
-use crate::device::Device;
-use crate::format::Format;
-use crate::format::FormatTy;
-use crate::image::sys::UnsafeImage;
-use crate::image::ImageAccess;
-use crate::image::ImageDimensions;
-use crate::memory::DeviceMemoryAllocError;
-use crate::sampler::Sampler;
-use crate::OomError;
-use crate::SafeDeref;
-use crate::VulkanObject;
-use std::error;
-use std::fmt;
-use std::hash::Hash;
-use std::hash::Hasher;
-use std::mem::MaybeUninit;
-use std::ops::Range;
-use std::ptr;
-use std::sync::Arc;
-
-/// A safe image view that checks for validity and keeps its attached image alive.
+use super::{
+ sys::Image, ImageAccess, ImageDimensions, ImageFormatInfo, ImageSubresourceRange, ImageUsage,
+};
+use crate::{
+ device::{Device, DeviceOwned},
+ format::{ChromaSampling, Format, FormatFeatures},
+ image::{ImageAspects, ImageCreateFlags, ImageTiling, ImageType, SampleCount},
+ macros::{impl_id_counter, vulkan_enum},
+ sampler::{ycbcr::SamplerYcbcrConversion, ComponentMapping},
+ OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject,
+};
+use std::{
+ error::Error,
+ fmt::{Debug, Display, Error as FmtError, Formatter},
+ hash::{Hash, Hasher},
+ mem::MaybeUninit,
+ num::NonZeroU64,
+ ptr,
+ sync::Arc,
+};
+
+/// A wrapper around an image that makes it available to shaders or framebuffers.
+#[derive(Debug)]
pub struct ImageView<I>
where
- I: ImageAccess,
+ I: ImageAccess + ?Sized,
{
- image: I,
- inner: UnsafeImageView,
- format: Format,
+ handle: ash::vk::ImageView,
+ image: Arc<I>,
+ id: NonZeroU64,
- ty: ImageViewType,
component_mapping: ComponentMapping,
- array_layers: Range<u32>,
+ format: Option<Format>,
+ format_features: FormatFeatures,
+ sampler_ycbcr_conversion: Option<Arc<SamplerYcbcrConversion>>,
+ subresource_range: ImageSubresourceRange,
+ usage: ImageUsage,
+ view_type: ImageViewType,
+
+ filter_cubic: bool,
+ filter_cubic_minmax: bool,
}
impl<I> ImageView<I>
where
- I: ImageAccess,
+ I: ImageAccess + ?Sized,
{
- /// Creates a default `ImageView`. Equivalent to `ImageView::start(image).build()`.
- #[inline]
- pub fn new(image: I) -> Result<Arc<ImageView<I>>, ImageViewCreationError> {
- Self::start(image).build()
- }
-
- /// Begins building an `ImageView`.
- pub fn start(image: I) -> ImageViewBuilder<I> {
- let ty = match image.dimensions() {
- ImageDimensions::Dim1d {
- array_layers: 1, ..
- } => ImageViewType::Dim1d,
- ImageDimensions::Dim1d { .. } => ImageViewType::Dim1dArray,
- ImageDimensions::Dim2d {
- array_layers: 1, ..
- } => ImageViewType::Dim2d,
- ImageDimensions::Dim2d { .. } => ImageViewType::Dim2dArray,
- ImageDimensions::Dim3d { .. } => ImageViewType::Dim3d,
- };
- let mipmap_levels = 0..image.mipmap_levels();
- let array_layers = 0..image.dimensions().array_layers();
+ /// Creates a new `ImageView`.
+ ///
+ /// # Panics
+ ///
+ /// - Panics if `create_info.array_layers` is empty.
+ /// - Panics if `create_info.mip_levels` is empty.
+ /// - Panics if `create_info.aspects` contains any aspects other than `color`, `depth`,
+ /// `stencil`, `plane0`, `plane1` or `plane2`.
+ /// - Panics if `create_info.aspects` contains more more than one aspect, unless `depth` and
+ /// `stencil` are the only aspects selected.
+ pub fn new(
+ image: Arc<I>,
+ create_info: ImageViewCreateInfo,
+ ) -> Result<Arc<ImageView<I>>, ImageViewCreationError> {
+ let format_features = Self::validate_new(&image, &create_info)?;
- ImageViewBuilder {
- image,
- ty,
- component_mapping: ComponentMapping::default(),
- mipmap_levels,
- array_layers,
+ unsafe {
+ Ok(Self::new_unchecked_with_format_features(
+ image,
+ create_info,
+ format_features,
+ )?)
}
}
- /// Returns the wrapped image that this image view was created from.
- pub fn image(&self) -> &I {
- &self.image
- }
-}
+ fn validate_new(
+ image: &I,
+ create_info: &ImageViewCreateInfo,
+ ) -> Result<FormatFeatures, ImageViewCreationError> {
+ let &ImageViewCreateInfo {
+ view_type,
+ format,
+ component_mapping,
+ ref subresource_range,
+ mut usage,
+ ref sampler_ycbcr_conversion,
+ _ne: _,
+ } = create_info;
-#[derive(Debug)]
-pub struct ImageViewBuilder<I> {
- image: I,
- ty: ImageViewType,
- component_mapping: ComponentMapping,
- mipmap_levels: Range<u32>,
- array_layers: Range<u32>,
-}
+ let image_inner = image.inner().image;
+ let device = image_inner.device();
+ let format = format.unwrap();
-impl<I> ImageViewBuilder<I>
-where
- I: ImageAccess,
-{
- /// Sets the image view type.
- ///
- /// By default, this is determined from the image, based on its dimensions and number of layers.
- /// The value of `ty` must be compatible with the dimensions of the image and the selected
- /// array layers.
- #[inline]
- pub fn with_type(mut self, ty: ImageViewType) -> Self {
- self.ty = ty;
- self
- }
+ let level_count = subresource_range.mip_levels.end - subresource_range.mip_levels.start;
+ let layer_count = subresource_range.array_layers.end - subresource_range.array_layers.start;
- /// Sets how to map components of each pixel.
- ///
- /// By default, this is the identity mapping, with every component mapped directly.
- #[inline]
- pub fn with_component_mapping(mut self, component_mapping: ComponentMapping) -> Self {
- self.component_mapping = component_mapping;
- self
- }
+ // VUID-VkImageSubresourceRange-aspectMask-requiredbitmask
+ assert!(!subresource_range.aspects.is_empty());
- /// Sets the range of mipmap levels that the view should cover.
- ///
- /// By default, this is the full range of mipmaps present in the image.
- #[inline]
- pub fn with_mipmap_levels(mut self, mipmap_levels: Range<u32>) -> Self {
- self.mipmap_levels = mipmap_levels;
- self
- }
+ // VUID-VkImageSubresourceRange-levelCount-01720
+ assert!(level_count != 0);
- /// Sets the range of array layers that the view should cover.
- ///
- /// By default, this is the full range of array layers present in the image.
- #[inline]
- pub fn with_array_layers(mut self, array_layers: Range<u32>) -> Self {
- self.array_layers = array_layers;
- self
- }
+ // VUID-VkImageSubresourceRange-layerCount-01721
+ assert!(layer_count != 0);
+
+ let default_usage = Self::get_default_usage(subresource_range.aspects, image_inner);
- /// Builds the `ImageView`.
- pub fn build(self) -> Result<Arc<ImageView<I>>, ImageViewCreationError> {
- let dimensions = self.image.dimensions();
- let format = self.image.format();
- let image_inner = self.image.inner().image;
- let usage = image_inner.usage();
- let flags = image_inner.flags();
+ let has_non_default_usage = if usage.is_empty() {
+ usage = default_usage;
+ false
+ } else {
+ usage == default_usage
+ };
- if self.mipmap_levels.end <= self.mipmap_levels.start
- || self.mipmap_levels.end > image_inner.mipmap_levels()
+ // VUID-VkImageViewCreateInfo-viewType-parameter
+ view_type.validate_device(device)?;
+
+ // VUID-VkImageViewCreateInfo-format-parameter
+ format.validate_device(device)?;
+
+ // VUID-VkComponentMapping-r-parameter
+ component_mapping.r.validate_device(device)?;
+
+ // VUID-VkComponentMapping-g-parameter
+ component_mapping.g.validate_device(device)?;
+
+ // VUID-VkComponentMapping-b-parameter
+ component_mapping.b.validate_device(device)?;
+
+ // VUID-VkComponentMapping-a-parameter
+ component_mapping.a.validate_device(device)?;
+
+ // VUID-VkImageSubresourceRange-aspectMask-parameter
+ subresource_range.aspects.validate_device(device)?;
+
+ assert!(!subresource_range.aspects.intersects(
+ ImageAspects::METADATA
+ | ImageAspects::MEMORY_PLANE_0
+ | ImageAspects::MEMORY_PLANE_1
+ | ImageAspects::MEMORY_PLANE_2
+ ));
+ assert!({
+ subresource_range.aspects.count() == 1
+ || subresource_range
+ .aspects
+ .contains(ImageAspects::DEPTH | ImageAspects::STENCIL)
+ && !subresource_range.aspects.intersects(
+ ImageAspects::COLOR
+ | ImageAspects::PLANE_0
+ | ImageAspects::PLANE_1
+ | ImageAspects::PLANE_2,
+ )
+ });
+
+ // Get format features
+ let format_features = unsafe { Self::get_format_features(format, image_inner) };
+
+ // No VUID apparently, but this seems like something we want to check?
+ if !image_inner
+ .format()
+ .unwrap()
+ .aspects()
+ .contains(subresource_range.aspects)
{
- return Err(ImageViewCreationError::MipMapLevelsOutOfRange);
+ return Err(ImageViewCreationError::ImageAspectsNotCompatible {
+ aspects: subresource_range.aspects,
+ image_aspects: image_inner.format().unwrap().aspects(),
+ });
+ }
+
+ // VUID-VkImageViewCreateInfo-None-02273
+ if format_features == FormatFeatures::default() {
+ return Err(ImageViewCreationError::FormatNotSupported);
+ }
+
+ // Check for compatibility with the image
+ let image_type = image.dimensions().image_type();
+
+ // VUID-VkImageViewCreateInfo-subResourceRange-01021
+ if !view_type.is_compatible_with(image_type) {
+ return Err(ImageViewCreationError::ImageTypeNotCompatible);
}
- if self.array_layers.end <= self.array_layers.start
- || self.array_layers.end > dimensions.array_layers()
+ // VUID-VkImageViewCreateInfo-image-01003
+ if (view_type == ImageViewType::Cube || view_type == ImageViewType::CubeArray)
+ && !image_inner
+ .flags()
+ .intersects(ImageCreateFlags::CUBE_COMPATIBLE)
{
- return Err(ImageViewCreationError::ArrayLayersOutOfRange);
+ return Err(ImageViewCreationError::ImageNotCubeCompatible);
+ }
+
+ // VUID-VkImageViewCreateInfo-viewType-01004
+ if view_type == ImageViewType::CubeArray && !device.enabled_features().image_cube_array {
+ return Err(ImageViewCreationError::RequirementNotMet {
+ required_for: "`create_info.viewtype` is `ImageViewType::CubeArray`",
+ requires_one_of: RequiresOneOf {
+ features: &["image_cube_array"],
+ ..Default::default()
+ },
+ });
}
- if !(usage.sampled
- || usage.storage
- || usage.color_attachment
- || usage.depth_stencil_attachment
- || usage.input_attachment
- || usage.transient_attachment)
+ // VUID-VkImageViewCreateInfo-subresourceRange-01718
+ if subresource_range.mip_levels.end > image_inner.mip_levels() {
+ return Err(ImageViewCreationError::MipLevelsOutOfRange {
+ range_end: subresource_range.mip_levels.end,
+ max: image_inner.mip_levels(),
+ });
+ }
+
+ if image_type == ImageType::Dim3d
+ && (view_type == ImageViewType::Dim2d || view_type == ImageViewType::Dim2dArray)
{
- return Err(ImageViewCreationError::InvalidImageUsage);
+ // VUID-VkImageViewCreateInfo-image-01005
+ if !image_inner
+ .flags()
+ .intersects(ImageCreateFlags::ARRAY_2D_COMPATIBLE)
+ {
+ return Err(ImageViewCreationError::ImageNotArray2dCompatible);
+ }
+
+ // VUID-VkImageViewCreateInfo-image-04970
+ if level_count != 1 {
+ return Err(ImageViewCreationError::Array2dCompatibleMultipleMipLevels);
+ }
+
+ // VUID-VkImageViewCreateInfo-image-02724
+ // VUID-VkImageViewCreateInfo-subresourceRange-02725
+ // We're using the depth dimension as array layers, but because of mip scaling, the
+ // depth, and therefore number of layers available, shrinks as the mip level gets
+ // higher.
+ let max = image_inner
+ .dimensions()
+ .mip_level_dimensions(subresource_range.mip_levels.start)
+ .unwrap()
+ .depth();
+ if subresource_range.array_layers.end > max {
+ return Err(ImageViewCreationError::ArrayLayersOutOfRange {
+ range_end: subresource_range.array_layers.end,
+ max,
+ });
+ }
+ } else {
+ // VUID-VkImageViewCreateInfo-image-01482
+ // VUID-VkImageViewCreateInfo-subresourceRange-01483
+ if subresource_range.array_layers.end > image_inner.dimensions().array_layers() {
+ return Err(ImageViewCreationError::ArrayLayersOutOfRange {
+ range_end: subresource_range.array_layers.end,
+ max: image_inner.dimensions().array_layers(),
+ });
+ }
}
- // Check for compatibility with the image
- match (
- self.ty,
- self.image.dimensions(),
- self.array_layers.end - self.array_layers.start,
- self.mipmap_levels.end - self.mipmap_levels.start,
- ) {
- (ImageViewType::Dim1d, ImageDimensions::Dim1d { .. }, 1, _) => (),
- (ImageViewType::Dim1dArray, ImageDimensions::Dim1d { .. }, _, _) => (),
- (ImageViewType::Dim2d, ImageDimensions::Dim2d { .. }, 1, _) => (),
- (ImageViewType::Dim2dArray, ImageDimensions::Dim2d { .. }, _, _) => (),
- (ImageViewType::Cubemap, ImageDimensions::Dim2d { .. }, 6, _)
- if flags.cube_compatible =>
+ // VUID-VkImageViewCreateInfo-image-04972
+ if image_inner.samples() != SampleCount::Sample1
+ && !(view_type == ImageViewType::Dim2d || view_type == ImageViewType::Dim2dArray)
+ {
+ return Err(ImageViewCreationError::MultisamplingNot2d);
+ }
+
+ /* Check usage requirements */
+
+ if has_non_default_usage {
+ if !(device.api_version() >= Version::V1_1
+ || device.enabled_extensions().khr_maintenance2)
{
- ()
+ return Err(ImageViewCreationError::RequirementNotMet {
+ required_for: "`create_info.usage` is not the default value",
+ requires_one_of: RequiresOneOf {
+ api_version: Some(Version::V1_1),
+ device_extensions: &["khr_maintenance2"],
+ ..Default::default()
+ },
+ });
}
- (ImageViewType::CubemapArray, ImageDimensions::Dim2d { .. }, n, _)
- if flags.cube_compatible && n % 6 == 0 =>
+
+ // VUID-VkImageViewUsageCreateInfo-usage-parameter
+ usage.validate_device(device)?;
+
+ // VUID-VkImageViewUsageCreateInfo-usage-requiredbitmask
+ assert!(!usage.is_empty());
+
+ // VUID-VkImageViewCreateInfo-pNext-02662
+ // VUID-VkImageViewCreateInfo-pNext-02663
+ // VUID-VkImageViewCreateInfo-pNext-02664
+ if !default_usage.contains(usage) {
+ return Err(ImageViewCreationError::UsageNotSupportedByImage {
+ usage,
+ supported_usage: default_usage,
+ });
+ }
+ }
+
+ // VUID-VkImageViewCreateInfo-image-04441
+ if !image_inner.usage().intersects(
+ ImageUsage::SAMPLED
+ | ImageUsage::STORAGE
+ | ImageUsage::COLOR_ATTACHMENT
+ | ImageUsage::DEPTH_STENCIL_ATTACHMENT
+ | ImageUsage::INPUT_ATTACHMENT
+ | ImageUsage::TRANSIENT_ATTACHMENT,
+ ) {
+ return Err(ImageViewCreationError::ImageMissingUsage);
+ }
+
+ // VUID-VkImageViewCreateInfo-usage-02274
+ if usage.intersects(ImageUsage::SAMPLED)
+ && !format_features.intersects(FormatFeatures::SAMPLED_IMAGE)
+ {
+ return Err(ImageViewCreationError::FormatUsageNotSupported { usage: "sampled" });
+ }
+
+ // VUID-VkImageViewCreateInfo-usage-02275
+ if usage.intersects(ImageUsage::STORAGE)
+ && !format_features.intersects(FormatFeatures::STORAGE_IMAGE)
+ {
+ return Err(ImageViewCreationError::FormatUsageNotSupported { usage: "storage" });
+ }
+
+ // VUID-VkImageViewCreateInfo-usage-02276
+ if usage.intersects(ImageUsage::COLOR_ATTACHMENT)
+ && !format_features.intersects(FormatFeatures::COLOR_ATTACHMENT)
+ {
+ return Err(ImageViewCreationError::FormatUsageNotSupported {
+ usage: "color_attachment",
+ });
+ }
+
+ // VUID-VkImageViewCreateInfo-usage-02277
+ if usage.intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
+ && !format_features.intersects(FormatFeatures::DEPTH_STENCIL_ATTACHMENT)
+ {
+ return Err(ImageViewCreationError::FormatUsageNotSupported {
+ usage: "depth_stencil_attachment",
+ });
+ }
+
+ // VUID-VkImageViewCreateInfo-usage-02652
+ if usage.intersects(ImageUsage::INPUT_ATTACHMENT)
+ && !format_features.intersects(
+ FormatFeatures::COLOR_ATTACHMENT | FormatFeatures::DEPTH_STENCIL_ATTACHMENT,
+ )
+ {
+ return Err(ImageViewCreationError::FormatUsageNotSupported {
+ usage: "input_attachment",
+ });
+ }
+
+ /* Check flags requirements */
+
+ if Some(format) != image_inner.format() {
+ // VUID-VkImageViewCreateInfo-image-01762
+ if !image_inner
+ .flags()
+ .intersects(ImageCreateFlags::MUTABLE_FORMAT)
+ || !image_inner.format().unwrap().planes().is_empty()
+ && subresource_range.aspects.intersects(ImageAspects::COLOR)
{
- ()
+ return Err(ImageViewCreationError::FormatNotCompatible);
}
- (ImageViewType::Dim3d, ImageDimensions::Dim3d { .. }, 1, _) => (),
- (ImageViewType::Dim2d, ImageDimensions::Dim3d { .. }, 1, 1)
- if flags.array_2d_compatible =>
+
+ // VUID-VkImageViewCreateInfo-imageViewFormatReinterpretation-04466
+ // TODO: it is unclear what the number of bits is for compressed formats.
+ // See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2361
+ if device.enabled_extensions().khr_portability_subset
+ && !device.enabled_features().image_view_format_reinterpretation
+ && format.components() != image_inner.format().unwrap().components()
{
- ()
+ return Err(ImageViewCreationError::RequirementNotMet {
+ required_for: "this device is a portability subset device, and the format of \
+ the image view does not have the same components and number of bits per \
+ component as the parent image",
+ requires_one_of: RequiresOneOf {
+ features: &["image_view_format_reinterpretation"],
+ ..Default::default()
+ },
+ });
}
- (ImageViewType::Dim2dArray, ImageDimensions::Dim3d { .. }, _, 1)
- if flags.array_2d_compatible =>
+
+ if image_inner
+ .flags()
+ .intersects(ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE)
{
- ()
+ // VUID-VkImageViewCreateInfo-image-01583
+ if !(format.compatibility() == image_inner.format().unwrap().compatibility()
+ || format.block_size() == image_inner.format().unwrap().block_size())
+ {
+ return Err(ImageViewCreationError::FormatNotCompatible);
+ }
+
+ if format.compression().is_none() {
+ // VUID-VkImageViewCreateInfo-image-01584
+ if layer_count != 1 {
+ return Err(
+ ImageViewCreationError::BlockTexelViewCompatibleMultipleArrayLayers,
+ );
+ }
+
+ // VUID-VkImageViewCreateInfo-image-01584
+ if level_count != 1 {
+ return Err(
+ ImageViewCreationError::BlockTexelViewCompatibleMultipleMipLevels,
+ );
+ }
+ }
+ } else {
+ if image_inner.format().unwrap().planes().is_empty() {
+ // VUID-VkImageViewCreateInfo-image-01761
+ if format.compatibility() != image_inner.format().unwrap().compatibility() {
+ return Err(ImageViewCreationError::FormatNotCompatible);
+ }
+ } else {
+ let plane = if subresource_range.aspects.intersects(ImageAspects::PLANE_0) {
+ 0
+ } else if subresource_range.aspects.intersects(ImageAspects::PLANE_1) {
+ 1
+ } else if subresource_range.aspects.intersects(ImageAspects::PLANE_2) {
+ 2
+ } else {
+ unreachable!()
+ };
+ let plane_format = image_inner.format().unwrap().planes()[plane];
+
+ // VUID-VkImageViewCreateInfo-image-01586
+ if format.compatibility() != plane_format.compatibility() {
+ return Err(ImageViewCreationError::FormatNotCompatible);
+ }
+ }
}
- _ => return Err(ImageViewCreationError::IncompatibleType),
}
- let inner = unsafe {
- UnsafeImageView::new(
- image_inner,
- self.ty,
- self.component_mapping,
- self.mipmap_levels,
- self.array_layers.clone(),
- )?
- };
-
- Ok(Arc::new(ImageView {
- image: self.image,
- inner,
- format,
-
- ty: self.ty,
- component_mapping: self.component_mapping,
- array_layers: self.array_layers,
- }))
- }
-}
+ // VUID-VkImageViewCreateInfo-imageViewType-04973
+ if (view_type == ImageViewType::Dim1d
+ || view_type == ImageViewType::Dim2d
+ || view_type == ImageViewType::Dim3d)
+ && layer_count != 1
+ {
+ return Err(ImageViewCreationError::TypeNonArrayedMultipleArrayLayers);
+ }
+ // VUID-VkImageViewCreateInfo-viewType-02960
+ else if view_type == ImageViewType::Cube && layer_count != 6 {
+ return Err(ImageViewCreationError::TypeCubeNot6ArrayLayers);
+ }
+ // VUID-VkImageViewCreateInfo-viewType-02961
+ else if view_type == ImageViewType::CubeArray && layer_count % 6 != 0 {
+ return Err(ImageViewCreationError::TypeCubeArrayNotMultipleOf6ArrayLayers);
+ }
-/// Error that can happen when creating an image view.
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub enum ImageViewCreationError {
- /// Allocating memory failed.
- AllocError(DeviceMemoryAllocError),
- /// The specified range of array layers was out of range for the image.
- ArrayLayersOutOfRange,
- /// The specified range of mipmap levels was out of range for the image.
- MipMapLevelsOutOfRange,
- /// The requested [`ImageViewType`] was not compatible with the image, or with the specified ranges of array layers and mipmap levels.
- IncompatibleType,
- /// The image was not created with
- /// [one of the required usages](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#valid-imageview-imageusage)
- /// for image views.
- InvalidImageUsage,
-}
+ // VUID-VkImageViewCreateInfo-imageViewFormatSwizzle-04465
+ if device.enabled_extensions().khr_portability_subset
+ && !device.enabled_features().image_view_format_swizzle
+ && !component_mapping.is_identity()
+ {
+ return Err(ImageViewCreationError::RequirementNotMet {
+ required_for: "this device is a portability subset device, and \
+ `create_info.component_mapping` is not the identity mapping",
+ requires_one_of: RequiresOneOf {
+ features: &["image_view_format_swizzle"],
+ ..Default::default()
+ },
+ });
+ }
-impl error::Error for ImageViewCreationError {
- #[inline]
- fn source(&self) -> Option<&(dyn error::Error + 'static)> {
- match *self {
- ImageViewCreationError::AllocError(ref err) => Some(err),
- _ => None,
+ // VUID-VkImageViewCreateInfo-format-04714
+ // VUID-VkImageViewCreateInfo-format-04715
+ match format.ycbcr_chroma_sampling() {
+ Some(ChromaSampling::Mode422) => {
+ if image_inner.dimensions().width() % 2 != 0 {
+ return Err(
+ ImageViewCreationError::FormatChromaSubsamplingInvalidImageDimensions,
+ );
+ }
+ }
+ Some(ChromaSampling::Mode420) => {
+ if image_inner.dimensions().width() % 2 != 0
+ || image_inner.dimensions().height() % 2 != 0
+ {
+ return Err(
+ ImageViewCreationError::FormatChromaSubsamplingInvalidImageDimensions,
+ );
+ }
+ }
+ _ => (),
}
- }
-}
-impl fmt::Display for ImageViewCreationError {
- #[inline]
- fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(
- fmt,
- "{}",
- match *self {
- ImageViewCreationError::AllocError(err) => "allocating memory failed",
- ImageViewCreationError::ArrayLayersOutOfRange => "array layers are out of range",
- ImageViewCreationError::MipMapLevelsOutOfRange => "mipmap levels are out of range",
- ImageViewCreationError::IncompatibleType =>
- "image view type is not compatible with image, array layers or mipmap levels",
- ImageViewCreationError::InvalidImageUsage =>
- "the usage of the image is not compatible with image views",
+ // Don't need to check features because you can't create a conversion object without the
+ // feature anyway.
+ if let Some(conversion) = &sampler_ycbcr_conversion {
+ assert_eq!(device, conversion.device());
+
+ // VUID-VkImageViewCreateInfo-pNext-01970
+ if !component_mapping.is_identity() {
+ return Err(
+ ImageViewCreationError::SamplerYcbcrConversionComponentMappingNotIdentity {
+ component_mapping,
+ },
+ );
+ }
+ } else {
+ // VUID-VkImageViewCreateInfo-format-06415
+ if format.ycbcr_chroma_sampling().is_some() {
+ return Err(
+ ImageViewCreationError::FormatRequiresSamplerYcbcrConversion { format },
+ );
}
- )
+ }
+
+ Ok(format_features)
}
-}
-impl From<OomError> for ImageViewCreationError {
- #[inline]
- fn from(err: OomError) -> ImageViewCreationError {
- ImageViewCreationError::AllocError(DeviceMemoryAllocError::OomError(err))
+ #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
+ pub unsafe fn new_unchecked(
+ image: Arc<I>,
+ create_info: ImageViewCreateInfo,
+ ) -> Result<Arc<Self>, VulkanError> {
+ let format_features =
+ Self::get_format_features(create_info.format.unwrap(), image.inner().image);
+ Self::new_unchecked_with_format_features(image, create_info, format_features)
}
-}
-/// A low-level wrapper around a `vkImageView`.
-pub struct UnsafeImageView {
- view: ash::vk::ImageView,
- device: Arc<Device>,
-}
+ unsafe fn new_unchecked_with_format_features(
+ image: Arc<I>,
+ create_info: ImageViewCreateInfo,
+ format_features: FormatFeatures,
+ ) -> Result<Arc<Self>, VulkanError> {
+ let &ImageViewCreateInfo {
+ view_type,
+ format,
+ component_mapping,
+ ref subresource_range,
+ mut usage,
+ ref sampler_ycbcr_conversion,
+ _ne: _,
+ } = &create_info;
+
+ let image_inner = image.inner().image;
+ let device = image_inner.device();
+
+ let default_usage = Self::get_default_usage(subresource_range.aspects, image_inner);
+
+ let has_non_default_usage = if usage.is_empty() {
+ usage = default_usage;
+ false
+ } else {
+ usage == default_usage
+ };
-impl UnsafeImageView {
- /// Creates a new view from an image.
- ///
- /// # Safety
- /// - The returned `UnsafeImageView` must not outlive `image`.
- /// - `image` must have a usage that is compatible with image views.
- /// - `ty` must be compatible with the dimensions and flags of the image.
- /// - `mipmap_levels` must not be empty, must be within the range of levels of the image, and be compatible with the requested `ty`.
- /// - `array_layers` must not be empty, must be within the range of layers of the image, and be compatible with the requested `ty`.
- ///
- /// # Panics
- /// Panics if the image is a YcbCr image, since the Vulkano API is not yet flexible enough to
- /// specify the aspect of image.
- pub unsafe fn new(
- image: &UnsafeImage,
- ty: ImageViewType,
- component_mapping: ComponentMapping,
- mipmap_levels: Range<u32>,
- array_layers: Range<u32>,
- ) -> Result<UnsafeImageView, OomError> {
- let fns = image.device().fns();
-
- debug_assert!(mipmap_levels.end > mipmap_levels.start);
- debug_assert!(mipmap_levels.end <= image.mipmap_levels());
- debug_assert!(array_layers.end > array_layers.start);
- debug_assert!(array_layers.end <= image.dimensions().array_layers());
-
- if image.format().ty() == FormatTy::Ycbcr {
- unimplemented!();
- }
-
- // TODO: Let user choose
- let aspects = image.format().aspects();
-
- let view = {
- let infos = ash::vk::ImageViewCreateInfo {
- flags: ash::vk::ImageViewCreateFlags::empty(),
- image: image.internal_object(),
- view_type: ty.into(),
- format: image.format().into(),
- components: component_mapping.into(),
- subresource_range: ash::vk::ImageSubresourceRange {
- aspect_mask: aspects.into(),
- base_mip_level: mipmap_levels.start,
- level_count: mipmap_levels.end - mipmap_levels.start,
- base_array_layer: array_layers.start,
- layer_count: array_layers.end - array_layers.start,
- },
+ let mut info_vk = ash::vk::ImageViewCreateInfo {
+ flags: ash::vk::ImageViewCreateFlags::empty(),
+ image: image_inner.handle(),
+ view_type: view_type.into(),
+ format: format.unwrap().into(),
+ components: component_mapping.into(),
+ subresource_range: subresource_range.clone().into(),
+ ..Default::default()
+ };
+ let mut image_view_usage_info_vk = None;
+ let mut sampler_ycbcr_conversion_info_vk = None;
+
+ if has_non_default_usage {
+ let next = image_view_usage_info_vk.insert(ash::vk::ImageViewUsageCreateInfo {
+ usage: usage.into(),
..Default::default()
- };
+ });
+
+ next.p_next = info_vk.p_next;
+ info_vk.p_next = next as *const _ as *const _;
+ }
+
+ if let Some(conversion) = sampler_ycbcr_conversion {
+ let next =
+ sampler_ycbcr_conversion_info_vk.insert(ash::vk::SamplerYcbcrConversionInfo {
+ conversion: conversion.handle(),
+ ..Default::default()
+ });
+ next.p_next = info_vk.p_next;
+ info_vk.p_next = next as *const _ as *const _;
+ }
+
+ let handle = {
+ let fns = device.fns();
let mut output = MaybeUninit::uninit();
- check_errors(fns.v1_0.create_image_view(
- image.device().internal_object(),
- &infos,
+ (fns.v1_0.create_image_view)(
+ device.handle(),
+ &info_vk,
ptr::null(),
output.as_mut_ptr(),
- ))?;
+ )
+ .result()
+ .map_err(VulkanError::from)?;
output.assume_init()
};
- Ok(UnsafeImageView {
- view,
- device: image.device().clone(),
- })
+ Self::from_handle_with_format_features(image, handle, create_info, format_features)
}
-}
-unsafe impl VulkanObject for UnsafeImageView {
- type Object = ash::vk::ImageView;
+ /// Creates a default `ImageView`. Equivalent to
+ /// `ImageView::new(image, ImageViewCreateInfo::from_image(image))`.
+ pub fn new_default(image: Arc<I>) -> Result<Arc<ImageView<I>>, ImageViewCreationError> {
+ let create_info = ImageViewCreateInfo::from_image(&image);
+ Self::new(image, create_info)
+ }
- #[inline]
- fn internal_object(&self) -> ash::vk::ImageView {
- self.view
+ /// Creates a new `ImageView` from a raw object handle.
+ ///
+ /// # Safety
+ ///
+ /// - `handle` must be a valid Vulkan object handle created from `image`.
+ /// - `create_info` must match the info used to create the object.
+ pub unsafe fn from_handle(
+ image: Arc<I>,
+ handle: ash::vk::ImageView,
+ create_info: ImageViewCreateInfo,
+ ) -> Result<Arc<Self>, VulkanError> {
+ let format_features =
+ Self::get_format_features(create_info.format.unwrap(), image.inner().image);
+ Self::from_handle_with_format_features(image, handle, create_info, format_features)
}
-}
-impl fmt::Debug for UnsafeImageView {
- #[inline]
- fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
- write!(fmt, "<Vulkan image view {:?}>", self.view)
+ unsafe fn from_handle_with_format_features(
+ image: Arc<I>,
+ handle: ash::vk::ImageView,
+ create_info: ImageViewCreateInfo,
+ format_features: FormatFeatures,
+ ) -> Result<Arc<Self>, VulkanError> {
+ let ImageViewCreateInfo {
+ view_type,
+ format,
+ component_mapping,
+ subresource_range,
+ mut usage,
+ sampler_ycbcr_conversion,
+ _ne: _,
+ } = create_info;
+
+ let image_inner = image.inner().image;
+ let device = image_inner.device();
+
+ if usage.is_empty() {
+ usage = Self::get_default_usage(subresource_range.aspects, image_inner);
+ }
+
+ let mut filter_cubic = false;
+ let mut filter_cubic_minmax = false;
+
+ if device
+ .physical_device()
+ .supported_extensions()
+ .ext_filter_cubic
+ {
+ // Use unchecked, because all validation has been done above or is validated by the
+ // image.
+ let properties =
+ device
+ .physical_device()
+ .image_format_properties_unchecked(ImageFormatInfo {
+ flags: image_inner.flags(),
+ format: image_inner.format(),
+ image_type: image.dimensions().image_type(),
+ tiling: image_inner.tiling(),
+ usage: image_inner.usage(),
+ image_view_type: Some(view_type),
+ ..Default::default()
+ })?;
+
+ if let Some(properties) = properties {
+ filter_cubic = properties.filter_cubic;
+ filter_cubic_minmax = properties.filter_cubic_minmax;
+ }
+ }
+
+ Ok(Arc::new(ImageView {
+ handle,
+ image,
+ id: Self::next_id(),
+ view_type,
+ format,
+ format_features,
+ component_mapping,
+ subresource_range,
+ usage,
+ sampler_ycbcr_conversion,
+ filter_cubic,
+ filter_cubic_minmax,
+ }))
+ }
+
+ // https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkImageViewCreateInfo.html#_description
+ fn get_default_usage(aspects: ImageAspects, image: &Image) -> ImageUsage {
+ let has_stencil_aspect = aspects.intersects(ImageAspects::STENCIL);
+ let has_non_stencil_aspect = !(aspects - ImageAspects::STENCIL).is_empty();
+
+ if has_stencil_aspect && has_non_stencil_aspect {
+ image.usage() & image.stencil_usage()
+ } else if has_stencil_aspect {
+ image.stencil_usage()
+ } else if has_non_stencil_aspect {
+ image.usage()
+ } else {
+ unreachable!()
+ }
+ }
+
+ // https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/chap12.html#resources-image-view-format-features
+ unsafe fn get_format_features(format: Format, image: &Image) -> FormatFeatures {
+ let device = image.device();
+
+ let mut format_features = if Some(format) != image.format() {
+ // Use unchecked, because all validation should have been done before calling.
+ let format_properties = device.physical_device().format_properties_unchecked(format);
+
+ match image.tiling() {
+ ImageTiling::Optimal => format_properties.optimal_tiling_features,
+ ImageTiling::Linear => format_properties.linear_tiling_features,
+ ImageTiling::DrmFormatModifier => format_properties.linear_tiling_features,
+ }
+ } else {
+ image.format_features()
+ };
+
+ if !device.enabled_extensions().khr_format_feature_flags2 {
+ if format.type_color().is_none()
+ && format_features.intersects(FormatFeatures::SAMPLED_IMAGE)
+ {
+ format_features |= FormatFeatures::SAMPLED_IMAGE_DEPTH_COMPARISON;
+ }
+
+ if format.shader_storage_image_without_format() {
+ if device
+ .enabled_features()
+ .shader_storage_image_read_without_format
+ {
+ format_features |= FormatFeatures::STORAGE_READ_WITHOUT_FORMAT;
+ }
+
+ if device
+ .enabled_features()
+ .shader_storage_image_write_without_format
+ {
+ format_features |= FormatFeatures::STORAGE_WRITE_WITHOUT_FORMAT;
+ }
+ }
+ }
+
+ format_features
+ }
+
+ /// Returns the wrapped image that this image view was created from.
+ pub fn image(&self) -> &Arc<I> {
+ &self.image
}
}
-impl Drop for UnsafeImageView {
- #[inline]
+impl<I> Drop for ImageView<I>
+where
+ I: ImageAccess + ?Sized,
+{
fn drop(&mut self) {
unsafe {
- let fns = self.device.fns();
- fns.v1_0
- .destroy_image_view(self.device.internal_object(), self.view, ptr::null());
+ let device = self.device();
+ let fns = device.fns();
+ (fns.v1_0.destroy_image_view)(device.handle(), self.handle, ptr::null());
}
}
}
-impl PartialEq for UnsafeImageView {
- #[inline]
- fn eq(&self, other: &Self) -> bool {
- self.view == other.view && self.device == other.device
+unsafe impl<I> VulkanObject for ImageView<I>
+where
+ I: ImageAccess + ?Sized,
+{
+ type Handle = ash::vk::ImageView;
+
+ fn handle(&self) -> Self::Handle {
+ self.handle
}
}
-impl Eq for UnsafeImageView {}
-
-impl Hash for UnsafeImageView {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.view.hash(state);
- self.device.hash(state);
+unsafe impl<I> DeviceOwned for ImageView<I>
+where
+ I: ImageAccess + ?Sized,
+{
+ fn device(&self) -> &Arc<Device> {
+ self.image.inner().image.device()
}
}
-/// The geometry type of an image view.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-#[repr(i32)]
-pub enum ImageViewType {
- Dim1d = ash::vk::ImageViewType::TYPE_1D.as_raw(),
- Dim1dArray = ash::vk::ImageViewType::TYPE_1D_ARRAY.as_raw(),
- Dim2d = ash::vk::ImageViewType::TYPE_2D.as_raw(),
- Dim2dArray = ash::vk::ImageViewType::TYPE_2D_ARRAY.as_raw(),
- Dim3d = ash::vk::ImageViewType::TYPE_3D.as_raw(),
- Cubemap = ash::vk::ImageViewType::CUBE.as_raw(),
- CubemapArray = ash::vk::ImageViewType::CUBE_ARRAY.as_raw(),
+impl_id_counter!(ImageView<I: ImageAccess + ?Sized>);
+
+/// Parameters to create a new `ImageView`.
+#[derive(Debug)]
+pub struct ImageViewCreateInfo {
+ /// The image view type.
+ ///
+ /// The view type must be compatible with the dimensions of the image and the selected array
+ /// layers.
+ ///
+ /// The default value is [`ImageViewType::Dim2d`].
+ pub view_type: ImageViewType,
+
+ /// The format of the image view.
+ ///
+ /// If this is set to a format that is different from the image, the image must be created with
+ /// the `mutable_format` flag.
+ ///
+ /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
+ /// devices, if `format` does not have the same number of components and bits per component as
+ /// the parent image's format, the
+ /// [`image_view_format_reinterpretation`](crate::device::Features::image_view_format_reinterpretation)
+ /// feature must be enabled on the device.
+ ///
+ /// The default value is `None`, which must be overridden.
+ pub format: Option<Format>,
+
+ /// How to map components of each pixel.
+ ///
+ /// On [portability subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
+ /// devices, if `component_mapping` is not the identity mapping, the
+ /// [`image_view_format_swizzle`](crate::device::Features::image_view_format_swizzle)
+ /// feature must be enabled on the device.
+ ///
+ /// The default value is [`ComponentMapping::identity()`].
+ pub component_mapping: ComponentMapping,
+
+ /// The subresource range of the image that the view should cover.
+ ///
+ /// The default value is empty, which must be overridden.
+ pub subresource_range: ImageSubresourceRange,
+
+ /// How the image view is going to be used.
+ ///
+ /// If `usage` is empty, then a default value is used based on the parent image's usages.
+ /// Depending on the image aspects selected in `subresource_range`,
+ /// the default `usage` will be equal to the parent image's `usage`, its `stencil_usage`,
+ /// or the intersection of the two.
+ ///
+ /// If you set `usage` to a different value from the default, then the device API version must
+ /// be at least 1.1, or the [`khr_maintenance2`](crate::device::DeviceExtensions::khr_maintenance2)
+ /// extension must be enabled on the device. The specified `usage` must be a subset of the
+ /// default value; usages that are not set for the parent image are not allowed.
+ ///
+ /// The default value is [`ImageUsage::empty()`].
+ pub usage: ImageUsage,
+
+ /// The sampler YCbCr conversion to be used with the image view.
+ ///
+ /// If set to `Some`, several restrictions apply:
+ /// - The `component_mapping` must be the identity swizzle for all components.
+ /// - If the image view is to be used in a shader, it must be in a combined image sampler
+ /// descriptor, a separate sampled image descriptor is not allowed.
+ /// - The corresponding sampler must have the same sampler YCbCr object or an identically
+ /// created one, and must be used as an immutable sampler within a descriptor set layout.
+ ///
+ /// The default value is `None`.
+ pub sampler_ycbcr_conversion: Option<Arc<SamplerYcbcrConversion>>,
+
+ pub _ne: crate::NonExhaustive,
}
-impl From<ImageViewType> for ash::vk::ImageViewType {
- fn from(val: ImageViewType) -> Self {
- Self::from_raw(val as i32)
+impl Default for ImageViewCreateInfo {
+ #[inline]
+ fn default() -> Self {
+ Self {
+ view_type: ImageViewType::Dim2d,
+ format: None,
+ component_mapping: ComponentMapping::identity(),
+ subresource_range: ImageSubresourceRange {
+ aspects: ImageAspects::empty(),
+ array_layers: 0..0,
+ mip_levels: 0..0,
+ },
+ usage: ImageUsage::empty(),
+ sampler_ycbcr_conversion: None,
+ _ne: crate::NonExhaustive(()),
+ }
}
}
-/// Specifies how the components of an image must be mapped.
-///
-/// When creating an image view, it is possible to ask the implementation to modify the value
-/// returned when accessing a given component from within a shader.
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
-pub struct ComponentMapping {
- /// First component.
- pub r: ComponentSwizzle,
- /// Second component.
- pub g: ComponentSwizzle,
- /// Third component.
- pub b: ComponentSwizzle,
- /// Fourth component.
- pub a: ComponentSwizzle,
+impl ImageViewCreateInfo {
+ /// Returns an `ImageViewCreateInfo` with the `view_type` determined from the image type and
+ /// array layers, and `subresource_range` determined from the image format and covering the
+ /// whole image.
+ pub fn from_image(image: &(impl ImageAccess + ?Sized)) -> Self {
+ Self {
+ view_type: match image.dimensions() {
+ ImageDimensions::Dim1d {
+ array_layers: 1, ..
+ } => ImageViewType::Dim1d,
+ ImageDimensions::Dim1d { .. } => ImageViewType::Dim1dArray,
+ ImageDimensions::Dim2d {
+ array_layers: 1, ..
+ } => ImageViewType::Dim2d,
+ ImageDimensions::Dim2d { .. } => ImageViewType::Dim2dArray,
+ ImageDimensions::Dim3d { .. } => ImageViewType::Dim3d,
+ },
+ format: Some(image.format()),
+ subresource_range: image.subresource_range(),
+ ..Default::default()
+ }
+ }
}
-impl ComponentMapping {
- /// Returns `true` if the component mapping is identity swizzled,
- /// meaning that all the members are `Identity`.
- ///
- /// Certain operations require views that are identity swizzled, and will return an error
- /// otherwise. For example, attaching a view to a framebuffer is only possible if the view is
- /// identity swizzled.
- #[inline]
- pub fn is_identity(&self) -> bool {
- self.r == ComponentSwizzle::Identity
- && self.g == ComponentSwizzle::Identity
- && self.b == ComponentSwizzle::Identity
- && self.a == ComponentSwizzle::Identity
+/// Error that can happen when creating an image view.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum ImageViewCreationError {
+ /// Allocating memory failed.
+ OomError(OomError),
+
+ RequirementNotMet {
+ required_for: &'static str,
+ requires_one_of: RequiresOneOf,
+ },
+
+ /// A 2D image view was requested from a 3D image, but a range of multiple mip levels was
+ /// specified.
+ Array2dCompatibleMultipleMipLevels,
+
+ /// The specified range of array layers was not a subset of those in the image.
+ ArrayLayersOutOfRange { range_end: u32, max: u32 },
+
+ /// The image has the `block_texel_view_compatible` flag, but a range of multiple array layers
+ /// was specified.
+ BlockTexelViewCompatibleMultipleArrayLayers,
+
+ /// The image has the `block_texel_view_compatible` flag, but a range of multiple mip levels
+ /// was specified.
+ BlockTexelViewCompatibleMultipleMipLevels,
+
+ /// The requested format has chroma subsampling, but the width and/or height of the image was
+ /// not a multiple of 2.
+ FormatChromaSubsamplingInvalidImageDimensions,
+
+ /// The requested format was not compatible with the image.
+ FormatNotCompatible,
+
+ /// The given format was not supported by the device.
+ FormatNotSupported,
+
+ /// The format requires a sampler YCbCr conversion, but none was provided.
+ FormatRequiresSamplerYcbcrConversion { format: Format },
+
+ /// A requested usage flag was not supported by the given format.
+ FormatUsageNotSupported { usage: &'static str },
+
+ /// An aspect was selected that was not present in the image.
+ ImageAspectsNotCompatible {
+ aspects: ImageAspects,
+ image_aspects: ImageAspects,
+ },
+
+ /// The image was not created with
+ /// [one of the required usages](https://registry.khronos.org/vulkan/specs/1.2-extensions/html/vkspec.html#valid-imageview-imageusage)
+ /// for image views.
+ ImageMissingUsage,
+
+ /// A 2D image view was requested from a 3D image, but the image was not created with the
+ /// `array_2d_compatible` flag.
+ ImageNotArray2dCompatible,
+
+ /// A cube image view type was requested, but the image was not created with the
+ /// `cube_compatible` flag.
+ ImageNotCubeCompatible,
+
+ /// The given image view type was not compatible with the type of the image.
+ ImageTypeNotCompatible,
+
+ /// The requested [`ImageViewType`] was not compatible with the image, or with the specified
+ /// ranges of array layers and mipmap levels.
+ IncompatibleType,
+
+ /// The specified range of mip levels was not a subset of those in the image.
+ MipLevelsOutOfRange { range_end: u32, max: u32 },
+
+ /// The image has multisampling enabled, but the image view type was not `Dim2d` or
+ /// `Dim2dArray`.
+ MultisamplingNot2d,
+
+ /// Sampler YCbCr conversion was enabled, but `component_mapping` was not the identity mapping.
+ SamplerYcbcrConversionComponentMappingNotIdentity { component_mapping: ComponentMapping },
+
+ /// The `CubeArray` image view type was specified, but the range of array layers did not have a
+ /// size that is a multiple 6.
+ TypeCubeArrayNotMultipleOf6ArrayLayers,
+
+ /// The `Cube` image view type was specified, but the range of array layers did not have a size
+ /// of 6.
+ TypeCubeNot6ArrayLayers,
+
+ /// A non-arrayed image view type was specified, but a range of multiple array layers was
+ /// specified.
+ TypeNonArrayedMultipleArrayLayers,
+
+ /// The provided `usage` is not supported by the parent image.
+ UsageNotSupportedByImage {
+ usage: ImageUsage,
+ supported_usage: ImageUsage,
+ },
+}
+
+impl Error for ImageViewCreationError {
+ fn source(&self) -> Option<&(dyn Error + 'static)> {
+ match self {
+ ImageViewCreationError::OomError(err) => Some(err),
+ _ => None,
+ }
}
}
-impl From<ComponentMapping> for ash::vk::ComponentMapping {
- #[inline]
- fn from(value: ComponentMapping) -> Self {
- Self {
- r: value.r.into(),
- g: value.g.into(),
- b: value.b.into(),
- a: value.a.into(),
+impl Display for ImageViewCreationError {
+ fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
+ match self {
+ Self::OomError(_) => write!(f, "allocating memory failed",),
+ Self::RequirementNotMet {
+ required_for,
+ requires_one_of,
+ } => write!(
+ f,
+ "a requirement was not met for: {}; requires one of: {}",
+ required_for, requires_one_of,
+ ),
+ Self::Array2dCompatibleMultipleMipLevels => write!(
+ f,
+ "a 2D image view was requested from a 3D image, but a range of multiple mip levels \
+ was specified",
+ ),
+ Self::ArrayLayersOutOfRange { .. } => write!(
+ f,
+ "the specified range of array layers was not a subset of those in the image",
+ ),
+ Self::BlockTexelViewCompatibleMultipleArrayLayers => write!(
+ f,
+ "the image has the `block_texel_view_compatible` flag, but a range of multiple \
+ array layers was specified",
+ ),
+ Self::BlockTexelViewCompatibleMultipleMipLevels => write!(
+ f,
+ "the image has the `block_texel_view_compatible` flag, but a range of multiple mip \
+ levels was specified",
+ ),
+ Self::FormatChromaSubsamplingInvalidImageDimensions => write!(
+ f,
+ "the requested format has chroma subsampling, but the width and/or height of the \
+ image was not a multiple of 2",
+ ),
+ Self::FormatNotCompatible => {
+ write!(f, "the requested format was not compatible with the image")
+ }
+ Self::FormatNotSupported => {
+ write!(f, "the given format was not supported by the device")
+ }
+ Self::FormatRequiresSamplerYcbcrConversion { .. } => write!(
+ f,
+ "the format requires a sampler YCbCr conversion, but none was provided",
+ ),
+ Self::FormatUsageNotSupported { .. } => write!(
+ f,
+ "a requested usage flag was not supported by the given format",
+ ),
+ Self::ImageAspectsNotCompatible { .. } => write!(
+ f,
+ "an aspect was selected that was not present in the image",
+ ),
+ Self::ImageMissingUsage => write!(
+ f,
+ "the image was not created with one of the required usages for image views",
+ ),
+ Self::ImageNotArray2dCompatible => write!(
+ f,
+ "a 2D image view was requested from a 3D image, but the image was not created with \
+ the `array_2d_compatible` flag",
+ ),
+ Self::ImageNotCubeCompatible => write!(
+ f,
+ "a cube image view type was requested, but the image was not created with the \
+ `cube_compatible` flag",
+ ),
+ Self::ImageTypeNotCompatible => write!(
+ f,
+ "the given image view type was not compatible with the type of the image",
+ ),
+ Self::IncompatibleType => write!(
+ f,
+ "image view type is not compatible with image, array layers or mipmap levels",
+ ),
+ Self::MipLevelsOutOfRange { .. } => write!(
+ f,
+ "the specified range of mip levels was not a subset of those in the image",
+ ),
+ Self::MultisamplingNot2d => write!(
+ f,
+ "the image has multisampling enabled, but the image view type was not `Dim2d` or \
+ `Dim2dArray`",
+ ),
+ Self::SamplerYcbcrConversionComponentMappingNotIdentity { .. } => write!(
+ f,
+ "sampler YCbCr conversion was enabled, but `component_mapping` was not the \
+ identity mapping",
+ ),
+ Self::TypeCubeArrayNotMultipleOf6ArrayLayers => write!(
+ f,
+ "the `CubeArray` image view type was specified, but the range of array layers did \
+ not have a size that is a multiple 6",
+ ),
+ Self::TypeCubeNot6ArrayLayers => write!(
+ f,
+ "the `Cube` image view type was specified, but the range of array layers did not \
+ have a size of 6",
+ ),
+ Self::TypeNonArrayedMultipleArrayLayers => write!(
+ f,
+ "a non-arrayed image view type was specified, but a range of multiple array layers \
+ was specified",
+ ),
+ Self::UsageNotSupportedByImage {
+ usage: _,
+ supported_usage: _,
+ } => write!(
+ f,
+ "the provided `usage` is not supported by the parent image",
+ ),
}
}
}
-/// Describes the value that an individual component must return when being accessed.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-#[repr(i32)]
-pub enum ComponentSwizzle {
- /// Returns the value that this component should normally have.
- ///
- /// This is the `Default` value.
- Identity = ash::vk::ComponentSwizzle::IDENTITY.as_raw(),
- /// Always return zero.
- Zero = ash::vk::ComponentSwizzle::ZERO.as_raw(),
- /// Always return one.
- One = ash::vk::ComponentSwizzle::ONE.as_raw(),
- /// Returns the value of the first component.
- Red = ash::vk::ComponentSwizzle::R.as_raw(),
- /// Returns the value of the second component.
- Green = ash::vk::ComponentSwizzle::G.as_raw(),
- /// Returns the value of the third component.
- Blue = ash::vk::ComponentSwizzle::B.as_raw(),
- /// Returns the value of the fourth component.
- Alpha = ash::vk::ComponentSwizzle::A.as_raw(),
+impl From<OomError> for ImageViewCreationError {
+ fn from(err: OomError) -> ImageViewCreationError {
+ ImageViewCreationError::OomError(err)
+ }
}
-impl From<ComponentSwizzle> for ash::vk::ComponentSwizzle {
- #[inline]
- fn from(val: ComponentSwizzle) -> Self {
- Self::from_raw(val as i32)
+impl From<VulkanError> for ImageViewCreationError {
+ fn from(err: VulkanError) -> ImageViewCreationError {
+ match err {
+ err @ VulkanError::OutOfHostMemory => OomError::from(err).into(),
+ err @ VulkanError::OutOfDeviceMemory => OomError::from(err).into(),
+ _ => panic!("unexpected error: {:?}", err),
+ }
}
}
-impl Default for ComponentSwizzle {
- #[inline]
- fn default() -> ComponentSwizzle {
- ComponentSwizzle::Identity
+impl From<RequirementNotMet> for ImageViewCreationError {
+ fn from(err: RequirementNotMet) -> Self {
+ Self::RequirementNotMet {
+ required_for: err.required_for,
+ requires_one_of: err.requires_one_of,
+ }
+ }
+}
+
+vulkan_enum! {
+ #[non_exhaustive]
+
+ /// The geometry type of an image view.
+ ImageViewType impl {
+ /// Returns whether the type is arrayed.
+ #[inline]
+ pub fn is_arrayed(self) -> bool {
+ match self {
+ Self::Dim1d | Self::Dim2d | Self::Dim3d | Self::Cube => false,
+ Self::Dim1dArray | Self::Dim2dArray | Self::CubeArray => true,
+ }
+ }
+
+ /// Returns whether `self` is compatible with the given `image_type`.
+ #[inline]
+ pub fn is_compatible_with(self, image_type: ImageType) -> bool {
+ matches!(
+ (self, image_type,),
+ (
+ ImageViewType::Dim1d | ImageViewType::Dim1dArray,
+ ImageType::Dim1d
+ ) | (
+ ImageViewType::Dim2d | ImageViewType::Dim2dArray,
+ ImageType::Dim2d | ImageType::Dim3d
+ ) | (
+ ImageViewType::Cube | ImageViewType::CubeArray,
+ ImageType::Dim2d
+ ) | (ImageViewType::Dim3d, ImageType::Dim3d)
+ )
+ }
}
+ = ImageViewType(i32);
+
+ // TODO: document
+ Dim1d = TYPE_1D,
+
+ // TODO: document
+ Dim2d = TYPE_2D,
+
+ // TODO: document
+ Dim3d = TYPE_3D,
+
+ // TODO: document
+ Cube = CUBE,
+
+ // TODO: document
+ Dim1dArray = TYPE_1D_ARRAY,
+
+ // TODO: document
+ Dim2dArray = TYPE_2D_ARRAY,
+
+ // TODO: document
+ CubeArray = CUBE_ARRAY,
}
/// Trait for types that represent the GPU can access an image view.
-pub unsafe trait ImageViewAbstract {
+pub unsafe trait ImageViewAbstract:
+ VulkanObject<Handle = ash::vk::ImageView> + DeviceOwned + Debug + Send + Sync
+{
/// Returns the wrapped image that this image view was created from.
- fn image(&self) -> &dyn ImageAccess;
+ fn image(&self) -> Arc<dyn ImageAccess>;
+
+ /// Returns the component mapping of this view.
+ fn component_mapping(&self) -> ComponentMapping;
+
+ /// Returns the dimensions of this view.
+ #[inline]
+ fn dimensions(&self) -> ImageDimensions {
+ let subresource_range = self.subresource_range();
+ let array_layers =
+ subresource_range.array_layers.end - subresource_range.array_layers.start;
+
+ match self.image().dimensions() {
+ ImageDimensions::Dim1d { width, .. } => ImageDimensions::Dim1d {
+ width,
+ array_layers,
+ },
+ ImageDimensions::Dim2d { width, height, .. } => ImageDimensions::Dim2d {
+ width,
+ height,
+ array_layers,
+ },
+ ImageDimensions::Dim3d {
+ width,
+ height,
+ depth,
+ } => ImageDimensions::Dim3d {
+ width,
+ height,
+ depth,
+ },
+ }
+ }
- /// Returns the inner unsafe image view object used by this image view.
- fn inner(&self) -> &UnsafeImageView;
+ /// Returns whether the image view supports sampling with a
+ /// [`Cubic`](crate::sampler::Filter::Cubic) `mag_filter` or `min_filter`.
+ fn filter_cubic(&self) -> bool;
- /// Returns the range of array layers of the wrapped image that this view exposes.
- fn array_layers(&self) -> Range<u32>;
+ /// Returns whether the image view supports 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`.
+ fn filter_cubic_minmax(&self) -> bool;
/// Returns the format of this view. This can be different from the parent's format.
- fn format(&self) -> Format;
+ fn format(&self) -> Option<Format>;
- /// Returns the component mapping of this view.
- fn component_mapping(&self) -> ComponentMapping;
+ /// Returns the features supported by the image view's format.
+ fn format_features(&self) -> FormatFeatures;
- /// Returns the [`ImageViewType`] of this image view.
- fn ty(&self) -> ImageViewType;
+ /// Returns the sampler YCbCr conversion that this image view was created with, if any.
+ fn sampler_ycbcr_conversion(&self) -> Option<&Arc<SamplerYcbcrConversion>>;
- /// Returns true if the given sampler can be used with this image view.
- ///
- /// This method should check whether the sampler's configuration can be used with the format
- /// of the view.
- // TODO: return a Result and propagate it when binding to a descriptor set
- fn can_be_sampled(&self, _sampler: &Sampler) -> bool {
- true /* FIXME */
- }
+ /// Returns the subresource range of the wrapped image that this view exposes.
+ fn subresource_range(&self) -> &ImageSubresourceRange;
+
+ /// Returns the usage of the image view.
+ fn usage(&self) -> ImageUsage;
+
+ /// Returns the [`ImageViewType`] of this image view.
+ fn view_type(&self) -> ImageViewType;
}
unsafe impl<I> ImageViewAbstract for ImageView<I>
where
- I: ImageAccess,
+ I: ImageAccess + Debug + 'static,
{
- #[inline]
- fn image(&self) -> &dyn ImageAccess {
- &self.image
+ fn image(&self) -> Arc<dyn ImageAccess> {
+ self.image.clone()
}
- #[inline]
- fn inner(&self) -> &UnsafeImageView {
- &self.inner
+ fn component_mapping(&self) -> ComponentMapping {
+ self.component_mapping
}
- #[inline]
- fn array_layers(&self) -> Range<u32> {
- self.array_layers.clone()
+ fn filter_cubic(&self) -> bool {
+ self.filter_cubic
}
- #[inline]
- fn format(&self) -> Format {
- // TODO: remove this default impl
+ fn filter_cubic_minmax(&self) -> bool {
+ self.filter_cubic_minmax
+ }
+
+ fn format(&self) -> Option<Format> {
self.format
}
+ fn format_features(&self) -> FormatFeatures {
+ self.format_features
+ }
+
+ fn sampler_ycbcr_conversion(&self) -> Option<&Arc<SamplerYcbcrConversion>> {
+ self.sampler_ycbcr_conversion.as_ref()
+ }
+
+ fn subresource_range(&self) -> &ImageSubresourceRange {
+ &self.subresource_range
+ }
+
+ fn usage(&self) -> ImageUsage {
+ self.usage
+ }
+
+ fn view_type(&self) -> ImageViewType {
+ self.view_type
+ }
+}
+
+unsafe impl ImageViewAbstract for ImageView<dyn ImageAccess> {
+ #[inline]
+ fn image(&self) -> Arc<dyn ImageAccess> {
+ self.image.clone()
+ }
+
#[inline]
fn component_mapping(&self) -> ComponentMapping {
self.component_mapping
}
#[inline]
- fn ty(&self) -> ImageViewType {
- self.ty
+ fn filter_cubic(&self) -> bool {
+ self.filter_cubic
}
-}
-unsafe impl<T> ImageViewAbstract for T
-where
- T: SafeDeref,
- T::Target: ImageViewAbstract,
-{
#[inline]
- fn image(&self) -> &dyn ImageAccess {
- (**self).image()
+ fn filter_cubic_minmax(&self) -> bool {
+ self.filter_cubic_minmax
}
#[inline]
- fn inner(&self) -> &UnsafeImageView {
- (**self).inner()
+ fn format(&self) -> Option<Format> {
+ self.format
}
#[inline]
- fn array_layers(&self) -> Range<u32> {
- (**self).array_layers()
+ fn format_features(&self) -> FormatFeatures {
+ self.format_features
}
#[inline]
- fn format(&self) -> Format {
- (**self).format()
+ fn sampler_ycbcr_conversion(&self) -> Option<&Arc<SamplerYcbcrConversion>> {
+ self.sampler_ycbcr_conversion.as_ref()
}
#[inline]
- fn component_mapping(&self) -> ComponentMapping {
- (**self).component_mapping()
+ fn subresource_range(&self) -> &ImageSubresourceRange {
+ &self.subresource_range
}
#[inline]
- fn ty(&self) -> ImageViewType {
- (**self).ty()
+ fn usage(&self) -> ImageUsage {
+ self.usage
}
#[inline]
- fn can_be_sampled(&self, sampler: &Sampler) -> bool {
- (**self).can_be_sampled(sampler)
+ fn view_type(&self) -> ImageViewType {
+ self.view_type
}
}
-impl PartialEq for dyn ImageViewAbstract + Send + Sync {
+impl PartialEq for dyn ImageViewAbstract {
#[inline]
fn eq(&self, other: &Self) -> bool {
- self.inner() == other.inner()
+ self.handle() == other.handle() && self.device() == other.device()
}
}
-impl Eq for dyn ImageViewAbstract + Send + Sync {}
+impl Eq for dyn ImageViewAbstract {}
-impl Hash for dyn ImageViewAbstract + Send + Sync {
- #[inline]
+impl Hash for dyn ImageViewAbstract {
fn hash<H: Hasher>(&self, state: &mut H) {
- self.inner().hash(state);
+ self.handle().hash(state);
+ self.device().hash(state);
}
}