From 467338c7781264deab2b4d193a4e5b8afa98299a Mon Sep 17 00:00:00 2001 From: Rua Date: Mon, 14 Aug 2023 12:22:29 +0200 Subject: [PATCH 1/2] Add support for image format lists --- vulkano/src/device/physical.rs | 20 +++ vulkano/src/image/mod.rs | 157 +++++++++++------- vulkano/src/image/sys.rs | 286 ++++++++++++++++++++++++--------- vulkano/src/image/view.rs | 121 +++++++------- vulkano/src/swapchain/mod.rs | 218 ++++++++++++++++++++----- 5 files changed, 575 insertions(+), 227 deletions(-) diff --git a/vulkano/src/device/physical.rs b/vulkano/src/device/physical.rs index f62c75dfc1..74c454a2a9 100644 --- a/vulkano/src/device/physical.rs +++ b/vulkano/src/device/physical.rs @@ -1029,6 +1029,7 @@ impl PhysicalDevice { external_memory_handle_type, image_view_type, ref drm_format_modifier_info, + ref view_formats, _ne: _, } = image_format_info; @@ -1042,6 +1043,8 @@ impl PhysicalDevice { }; let mut drm_format_modifier_info_vk = None; let mut external_info_vk = None; + let mut format_list_info_vk = None; + let format_list_view_formats_vk: Vec<_>; let mut image_view_info_vk = None; let mut stencil_usage_info_vk = None; @@ -1087,6 +1090,23 @@ impl PhysicalDevice { info2_vk.p_next = next as *const _ as *const _; } + if !view_formats.is_empty() { + format_list_view_formats_vk = view_formats + .iter() + .copied() + .map(ash::vk::Format::from) + .collect(); + + let next = format_list_info_vk.insert(ash::vk::ImageFormatListCreateInfo { + view_format_count: format_list_view_formats_vk.len() as u32, + p_view_formats: format_list_view_formats_vk.as_ptr(), + ..Default::default() + }); + + next.p_next = info2_vk.p_next; + info2_vk.p_next = next as *const _ as *const _; + } + if let Some(image_view_type) = image_view_type { let next = image_view_info_vk.insert( ash::vk::PhysicalDeviceImageViewImageFormatInfoEXT { diff --git a/vulkano/src/image/mod.rs b/vulkano/src/image/mod.rs index 297dcc80b0..ba0fcc99d9 100644 --- a/vulkano/src/image/mod.rs +++ b/vulkano/src/image/mod.rs @@ -207,10 +207,12 @@ impl Image { swapchain: Arc, image_index: u32, ) -> Result { + // Per https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCreateSwapchainKHR.html#_description let create_info = ImageCreateInfo { - flags: ImageCreateFlags::empty(), + flags: swapchain.flags().into(), image_type: ImageType::Dim2d, format: swapchain.image_format(), + view_formats: swapchain.image_view_formats().to_vec(), extent: [swapchain.image_extent()[0], swapchain.image_extent()[1], 1], array_layers: swapchain.image_array_layers(), mip_levels: 1, @@ -220,7 +222,10 @@ impl Image { stencil_usage: None, sharing: swapchain.image_sharing().clone(), initial_layout: ImageLayout::Undefined, - ..Default::default() + drm_format_modifiers: Vec::new(), + drm_format_modifier_plane_layouts: Vec::new(), + external_memory_handle_types: ExternalMemoryHandleTypes::empty(), + _ne: crate::NonExhaustive(()), }; Ok(Self::from_raw( @@ -279,6 +284,12 @@ impl Image { self.inner.format_features() } + /// Returns the formats that an image view created from this image can have. + #[inline] + pub fn view_formats(&self) -> &[Format] { + self.inner.view_formats() + } + /// Returns the extent of the image. #[inline] pub fn extent(&self) -> [u32; 3] { @@ -971,7 +982,7 @@ impl FusedIterator for SubresourceRangeIterator {} vulkan_bitflags! { #[non_exhaustive] - /// Flags that can be set when creating a new image. + /// Flags specifying additional properties of an image. ImageCreateFlags = ImageCreateFlags(u32); /* TODO: enable @@ -1647,6 +1658,16 @@ pub struct ImageFormatInfo { /// The default value is `Format::UNDEFINED`. pub format: Format, + /// The image view formats that will be allowed for the image. + /// + /// If this is not empty, then the physical device API version must be at least 1.2, or the + /// [`khr_image_format_list`] extension must be supported by the physical device. + /// + /// The default value is empty. + /// + /// [`khr_image_format_list`]: crate::device::DeviceExtensions::khr_image_format_list + pub view_formats: Vec, + /// The dimension type that the image will have. /// /// The default value is [`ImageType::Dim2d`]. @@ -1671,6 +1692,15 @@ pub struct ImageFormatInfo { /// The default value is `None`. pub stencil_usage: Option, + /// The Linux DRM format modifier information to query. + /// + /// If this is `Some`, then the + /// [`ext_image_drm_format_modifier`](crate::device::DeviceExtensions::ext_image_drm_format_modifier) + /// extension must be supported by the physical device. + /// + /// The default value is `None`. + pub drm_format_modifier_info: Option, + /// An external memory handle type that will be imported to or exported from the image. /// /// This is needed to retrieve the @@ -1693,15 +1723,6 @@ pub struct ImageFormatInfo { /// The default value is `None`. pub image_view_type: Option, - /// The Linux DRM format modifier information to query. - /// - /// If this is `Some`, then the - /// [`ext_image_drm_format_modifier`](crate::device::DeviceExtensions::ext_image_drm_format_modifier) - /// extension must be supported by the physical device. - /// - /// The default value is `None`. - pub drm_format_modifier_info: Option, - pub _ne: crate::NonExhaustive, } @@ -1711,13 +1732,14 @@ impl Default for ImageFormatInfo { Self { flags: ImageCreateFlags::empty(), format: Format::UNDEFINED, + view_formats: Vec::new(), image_type: ImageType::Dim2d, tiling: ImageTiling::Optimal, usage: ImageUsage::empty(), stencil_usage: None, + drm_format_modifier_info: None, external_memory_handle_type: None, image_view_type: None, - drm_format_modifier_info: None, _ne: crate::NonExhaustive(()), } } @@ -1731,13 +1753,14 @@ impl ImageFormatInfo { let &Self { flags, format, + ref view_formats, image_type, tiling, usage, stencil_usage, + ref drm_format_modifier_info, external_memory_handle_type, image_view_type, - ref drm_format_modifier_info, _ne: _, } = self; @@ -1837,52 +1860,29 @@ impl ImageFormatInfo { } } - if let Some(handle_type) = external_memory_handle_type { - if !(physical_device.api_version() >= Version::V1_1 - || physical_device - .instance() - .enabled_extensions() - .khr_external_memory_capabilities) + if !view_formats.is_empty() { + if !(physical_device.api_version() >= Version::V1_2 + || physical_device.supported_extensions().khr_image_format_list) { return Err(Box::new(ValidationError { - problem: "`external_memory_handle_type` is `Some`".into(), + context: "view_formats".into(), + problem: "is not empty".into(), requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]), - RequiresAllOf(&[Requires::InstanceExtension( - "khr_external_memory_capabilities", - )]), + RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]), + RequiresAllOf(&[Requires::DeviceExtension("khr_image_format_list")]), ]), ..Default::default() })); } - handle_type - .validate_physical_device(physical_device) - .map_err(|err| { - err.add_context("handle_type").set_vuids(&[ - "VUID-VkPhysicalDeviceExternalImageFormatInfo-handleType-parameter", - ]) - })?; - } - - if let Some(image_view_type) = image_view_type { - if !physical_device.supported_extensions().ext_filter_cubic { - return Err(Box::new(ValidationError { - problem: "`image_view_type` is `Some`".into(), - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( - "ext_filter_cubic", - )])]), - ..Default::default() - })); + for (index, view_format) in view_formats.iter().enumerate() { + view_format + .validate_physical_device(physical_device) + .map_err(|err| { + err.add_context(format!("view_formats[{}]", index)) + .set_vuids(&["VUID-VkImageFormatListCreateInfo-pViewFormats-parameter"]) + })?; } - - image_view_type - .validate_physical_device(physical_device) - .map_err(|err| { - err.add_context("image_view_type").set_vuids(&[ - "VUID-VkPhysicalDeviceImageViewImageFormatInfoEXT-imageViewType-parameter", - ]) - })?; } if let Some(drm_format_modifier_info) = drm_format_modifier_info { @@ -1914,10 +1914,11 @@ impl ImageFormatInfo { })); } - if flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) { + if flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) && view_formats.is_empty() { return Err(Box::new(ValidationError { - problem: "`tiling` is `ImageTiling::DrmFormatModifier`, but \ - `flags` contains `ImageCreateFlags::MUTABLE_FORMAT`" + problem: "`tiling` is `ImageTiling::DrmFormatModifier`, and \ + `flags` contains `ImageCreateFlags::MUTABLE_FORMAT`, but \ + `view_formats` is empty" .into(), vuids: &["VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02313"], ..Default::default() @@ -1933,6 +1934,54 @@ impl ImageFormatInfo { })); } + if let Some(handle_type) = external_memory_handle_type { + if !(physical_device.api_version() >= Version::V1_1 + || physical_device + .instance() + .enabled_extensions() + .khr_external_memory_capabilities) + { + return Err(Box::new(ValidationError { + problem: "`external_memory_handle_type` is `Some`".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]), + RequiresAllOf(&[Requires::InstanceExtension( + "khr_external_memory_capabilities", + )]), + ]), + ..Default::default() + })); + } + + handle_type + .validate_physical_device(physical_device) + .map_err(|err| { + err.add_context("handle_type").set_vuids(&[ + "VUID-VkPhysicalDeviceExternalImageFormatInfo-handleType-parameter", + ]) + })?; + } + + if let Some(image_view_type) = image_view_type { + if !physical_device.supported_extensions().ext_filter_cubic { + return Err(Box::new(ValidationError { + problem: "`image_view_type` is `Some`".into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension( + "ext_filter_cubic", + )])]), + ..Default::default() + })); + } + + image_view_type + .validate_physical_device(physical_device) + .map_err(|err| { + err.add_context("image_view_type").set_vuids(&[ + "VUID-VkPhysicalDeviceImageViewImageFormatInfoEXT-imageViewType-parameter", + ]) + })?; + } + Ok(()) } } diff --git a/vulkano/src/image/sys.rs b/vulkano/src/image/sys.rs index ef8dd03508..7e3e38df8f 100644 --- a/vulkano/src/image/sys.rs +++ b/vulkano/src/image/sys.rs @@ -63,6 +63,7 @@ pub struct RawImage { image_type: ImageType, format: Format, format_features: FormatFeatures, + view_formats: Vec, extent: [u32; 3], array_layers: u32, mip_levels: u32, @@ -113,6 +114,7 @@ impl RawImage { flags, image_type, format, + ref view_formats, extent, array_layers, mip_levels, @@ -122,10 +124,10 @@ impl RawImage { stencil_usage, ref sharing, initial_layout, - external_memory_handle_types, - _ne: _, ref drm_format_modifiers, ref drm_format_modifier_plane_layouts, + external_memory_handle_types, + _ne: _, } = &create_info; let (sharing_mode, queue_family_index_count, p_queue_family_indices) = match sharing { @@ -137,7 +139,7 @@ impl RawImage { ), }; - let mut info_vk = ash::vk::ImageCreateInfo { + let mut create_info_vk = ash::vk::ImageCreateInfo { flags: flags.into(), image_type: image_type.into(), format: format.into(), @@ -161,6 +163,8 @@ impl RawImage { let drm_format_modifier_plane_layouts_vk: SmallVec<[_; 4]>; let mut drm_format_modifier_list_info_vk = None; let mut external_memory_info_vk = None; + let mut format_list_info_vk = None; + let format_list_view_formats_vk: Vec<_>; let mut stencil_usage_info_vk = None; #[allow(clippy::comparison_chain)] @@ -196,8 +200,8 @@ impl RawImage { }, ); - next.p_next = info_vk.p_next; - info_vk.p_next = next as *const _ as *const _; + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; } else if drm_format_modifiers.len() > 1 { let next = drm_format_modifier_list_info_vk.insert( ash::vk::ImageDrmFormatModifierListCreateInfoEXT { @@ -207,8 +211,8 @@ impl RawImage { }, ); - next.p_next = info_vk.p_next; - info_vk.p_next = next as *const _ as *const _; + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; } if !external_memory_handle_types.is_empty() { @@ -217,8 +221,25 @@ impl RawImage { ..Default::default() }); - next.p_next = info_vk.p_next; - info_vk.p_next = next as *const _ as *const _; + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; + } + + if !view_formats.is_empty() { + format_list_view_formats_vk = view_formats + .iter() + .copied() + .map(ash::vk::Format::from) + .collect(); + + let next = format_list_info_vk.insert(ash::vk::ImageFormatListCreateInfo { + view_format_count: format_list_view_formats_vk.len() as u32, + p_view_formats: format_list_view_formats_vk.as_ptr(), + ..Default::default() + }); + + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; } if let Some(stencil_usage) = stencil_usage { @@ -227,16 +248,21 @@ impl RawImage { ..Default::default() }); - next.p_next = info_vk.p_next; - info_vk.p_next = next as *const _ as *const _; + next.p_next = create_info_vk.p_next; + create_info_vk.p_next = next as *const _ as *const _; } let handle = { let fns = device.fns(); let mut output = MaybeUninit::uninit(); - (fns.v1_0.create_image)(device.handle(), &info_vk, ptr::null(), output.as_mut_ptr()) - .result() - .map_err(VulkanError::from)?; + (fns.v1_0.create_image)( + device.handle(), + &create_info_vk, + ptr::null(), + output.as_mut_ptr(), + ) + .result() + .map_err(VulkanError::from)?; output.assume_init() }; @@ -269,6 +295,7 @@ impl RawImage { flags, image_type, format, + view_formats, extent, array_layers, mip_levels, @@ -278,10 +305,10 @@ impl RawImage { stencil_usage, sharing, initial_layout, - external_memory_handle_types, - _ne: _, drm_format_modifiers: _, drm_format_modifier_plane_layouts: _, + external_memory_handle_types, + _ne: _, } = create_info; let format_properties = @@ -337,6 +364,7 @@ impl RawImage { image_type, format, format_features, + view_formats, extent, array_layers, mip_levels, @@ -927,12 +955,11 @@ impl RawImage { physical_device.image_format_properties_unchecked(ImageFormatInfo { flags: self.flags, format: self.format, + view_formats: self.view_formats.clone(), image_type: self.image_type, tiling: self.tiling, usage: self.usage, stencil_usage: self.stencil_usage, - external_memory_handle_type: Some(handle_type), - image_view_type: None, drm_format_modifier_info: self.drm_format_modifier().map( |(drm_format_modifier, _)| ImageDrmFormatModifierInfo { drm_format_modifier, @@ -940,6 +967,8 @@ impl RawImage { _ne: crate::NonExhaustive(()), }, ), + external_memory_handle_type: Some(handle_type), + image_view_type: None, _ne: crate::NonExhaustive(()), }) } @@ -1198,6 +1227,12 @@ impl RawImage { self.format_features } + /// Returns the formats that an image view created from this image can have. + #[inline] + pub fn view_formats(&self) -> &[Format] { + &self.view_formats + } + /// Returns the extent of the image. #[inline] pub fn extent(&self) -> [u32; 3] { @@ -1602,9 +1637,9 @@ impl_id_counter!(RawImage); /// Parameters to create a new `Image`. #[derive(Clone, Debug)] pub struct ImageCreateInfo { - /// Flags to enable. + /// Additional properties of the image. /// - /// The default value is [`ImageCreateFlags::empty()`]. + /// The default value is empty. pub flags: ImageCreateFlags, /// The basic image dimensionality to create the image with. @@ -1617,6 +1652,27 @@ pub struct ImageCreateInfo { /// The default value is `Format::UNDEFINED`. pub format: Format, + /// The additional formats that an image view can have when it is created from this image. + /// + /// If the list is not empty, and `flags` does not contain [`ImageCreateFlags::MUTABLE_FORMAT`], + /// then the list must contain at most one element, otherwise any number of elements are + /// allowed. The view formats must be compatible with `format`. If `flags` also contains + /// [`ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`], then the view formats can also be + /// uncompressed formats that are merely size-compatible with `format`. + /// + /// If the list is empty, then depending on `flags`, a view must have the same format as + /// `format`, can have any compatible format, or additionally any uncompressed size-compatible + /// format. However, this is less efficient than specifying the possible view formats + /// in advance. + /// + /// If this is not empty, then the device API version must be at least 1.2, or the + /// [`khr_image_format_list`] extension must be enabled on the device. + /// + /// The default value is empty. + /// + /// [`khr_image_format_list`]: crate::device::DeviceExtensions::khr_image_format_list + pub view_formats: Vec, + /// The width, height and depth of the image. /// /// If `image_type` is `ImageType::Dim2d`, then the depth must be 1. @@ -1657,7 +1713,7 @@ pub struct ImageCreateInfo { /// How the image is going to be used. /// - /// The default value is [`ImageUsage::empty()`], which must be overridden. + /// The default value is empty, which must be overridden. pub usage: ImageUsage, /// How the stencil aspect of the image is going to be used, if different from the regular @@ -1680,16 +1736,6 @@ pub struct ImageCreateInfo { /// The default value is [`ImageLayout::Undefined`]. pub initial_layout: ImageLayout, - /// The external memory handle types that are going to be used with the image. - /// - /// If any of the fields in this value are set, the device must either support API version 1.1 - /// or the [`khr_external_memory`](crate::device::DeviceExtensions::khr_external_memory) - /// extension must be enabled, and `initial_layout` must be set to - /// [`ImageLayout::Undefined`]. - /// - /// The default value is [`ExternalMemoryHandleTypes::empty()`]. - pub external_memory_handle_types: ExternalMemoryHandleTypes, - /// The Linux DRM format modifiers that the image should be created with. /// /// If this is not empty, then the @@ -1697,7 +1743,7 @@ pub struct ImageCreateInfo { /// extension must be enabled on the device. /// /// The default value is empty. - pub drm_format_modifiers: SmallVec<[u64; 1]>, + pub drm_format_modifiers: Vec, /// If `drm_format_modifiers` contains one element, provides the subresource layouts of each /// memory plane of the image. The number of elements must equal @@ -1708,11 +1754,22 @@ pub struct ImageCreateInfo { /// - If `image_type` is not [`ImageType::Dim3d`] or `extent[2]` is 1, then /// [`SubresourceLayout::depth_pitch`] must be `None`. /// - /// If `drm_format_modifiers` does not contain one element, then - /// this must be empty. + /// If `drm_format_modifiers` does not contain one element, then this must be empty. + /// + /// The default value is empty. /// /// [`DrmFormatModifierProperties::drm_format_modifier_plane_count`]: crate::format::DrmFormatModifierProperties::drm_format_modifier_plane_count - pub drm_format_modifier_plane_layouts: SmallVec<[SubresourceLayout; 4]>, + pub drm_format_modifier_plane_layouts: Vec, + + /// The external memory handle types that are going to be used with the image. + /// + /// If this is not empty, then the device API version must be at least 1.1, or the + /// [`khr_external_memory`](crate::device::DeviceExtensions::khr_external_memory) + /// extension must be enabled on the device. `initial_layout` must be set to + /// [`ImageLayout::Undefined`]. + /// + /// The default value is empty. + pub external_memory_handle_types: ExternalMemoryHandleTypes, pub _ne: crate::NonExhaustive, } @@ -1724,6 +1781,7 @@ impl Default for ImageCreateInfo { flags: ImageCreateFlags::empty(), image_type: ImageType::Dim2d, format: Format::UNDEFINED, + view_formats: Vec::new(), extent: [0; 3], array_layers: 1, mip_levels: 1, @@ -1734,8 +1792,8 @@ impl Default for ImageCreateInfo { sharing: Sharing::Exclusive, initial_layout: ImageLayout::Undefined, external_memory_handle_types: ExternalMemoryHandleTypes::empty(), - drm_format_modifiers: SmallVec::new(), - drm_format_modifier_plane_layouts: SmallVec::new(), + drm_format_modifiers: Vec::new(), + drm_format_modifier_plane_layouts: Vec::new(), _ne: crate::NonExhaustive(()), } } @@ -1747,6 +1805,7 @@ impl ImageCreateInfo { flags, image_type, format, + ref view_formats, extent, array_layers, mip_levels, @@ -1756,10 +1815,10 @@ impl ImageCreateInfo { stencil_usage, ref sharing, initial_layout, - external_memory_handle_types, - _ne: _, ref drm_format_modifiers, ref drm_format_modifier_plane_layouts, + external_memory_handle_types, + _ne: _, } = self; let physical_device = device.physical_device(); @@ -1851,6 +1910,76 @@ impl ImageCreateInfo { })); } + if !view_formats.is_empty() { + if !(device.api_version() >= Version::V1_2 + || device.enabled_extensions().khr_image_format_list) + { + return Err(Box::new(ValidationError { + context: "view_formats".into(), + problem: "is not empty".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]), + RequiresAllOf(&[Requires::DeviceExtension("khr_image_format_list")]), + ]), + ..Default::default() + })); + } + + if !flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) && view_formats.len() != 1 { + return Err(Box::new(ValidationError { + problem: "`flags` does not contain `ImageCreateFlags::MUTABLE_FORMAT`, but \ + `view_formats` contains more than one element" + .into(), + vuids: &["VUID-VkImageCreateInfo-flags-04738"], + ..Default::default() + })); + } + + for (index, &view_format) in view_formats.iter().enumerate() { + view_format.validate_device(device).map_err(|err| { + err.add_context(format!("view_formats[{}]", index)) + .set_vuids(&["VUID-VkImageFormatListCreateInfo-pViewFormats-parameter"]) + })?; + + if flags.intersects(ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE) + && view_format.compression().is_none() + { + if !(view_format.compatibility() == format.compatibility() + || view_format.block_size() == format.block_size()) + { + return Err(Box::new(ValidationError { + problem: format!( + "`flags` contains \ + `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, and \ + `view_formats[{}]` is an uncompressed format, but \ + it is not compatible with `format`, and \ + does not have an equal block size", + index + ) + .into(), + vuids: &["VUID-VkImageCreateInfo-pNext-06722"], + ..Default::default() + })); + } + } else { + if view_format.compatibility() != format.compatibility() { + return Err(Box::new(ValidationError { + problem: format!( + "`flags` does not contain \ + `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, or \ + `view_format[{}]` is a compressed format, but \ + it is not compatible with `create_info.format`", + index + ) + .into(), + vuids: &["VUID-VkImageCreateInfo-pNext-06722"], + ..Default::default() + })); + } + } + } + } + match image_type { ImageType::Dim1d => { if extent[1] != 1 { @@ -2407,7 +2536,7 @@ impl ImageCreateInfo { { return Err(Box::new(ValidationError { problem: "`flags` contains `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, \ - but `format` is not a compressed format" + but `format` is not a compressed format" .into(), vuids: &["VUID-VkImageCreateInfo-flags-01572"], ..Default::default() @@ -2418,7 +2547,7 @@ impl ImageCreateInfo { if format.planes().len() < 2 { return Err(Box::new(ValidationError { problem: "`flags` contains `ImageCreateFlags::DISJOINT`, but `format` \ - is not a multi-planat format" + is not a multi-planat format" .into(), vuids: &["VUID-VkImageCreateInfo-format-01577"], ..Default::default() @@ -2482,41 +2611,6 @@ impl ImageCreateInfo { } } - /* External memory handles */ - - if !external_memory_handle_types.is_empty() { - if !(device.api_version() >= Version::V1_1 - || device.enabled_extensions().khr_external_memory) - { - return Err(Box::new(ValidationError { - context: "external_memory_handle_types".into(), - problem: "is not empty".into(), - requires_one_of: RequiresOneOf(&[ - RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]), - RequiresAllOf(&[Requires::DeviceExtension("khr_external_memory")]), - ]), - ..Default::default() - })); - } - - external_memory_handle_types - .validate_device(device) - .map_err(|err| { - err.add_context("external_memory_handle_types") - .set_vuids(&["VUID-VkExternalMemoryImageCreateInfo-handleTypes-parameter"]) - })?; - - if initial_layout != ImageLayout::Undefined { - return Err(Box::new(ValidationError { - problem: "`external_memory_handle_types` is not empty, but \ - `initial_layout` is not `ImageLayout::Undefined`" - .into(), - vuids: &["VUID-VkImageCreateInfo-pNext-01443"], - ..Default::default() - })); - } - } - if !drm_format_modifiers.is_empty() { // This implicitly checks for the enabled extension too, // so no need to check that separately. @@ -2530,10 +2624,11 @@ impl ImageCreateInfo { })); } - if flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) { + if flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) && view_formats.is_empty() { return Err(Box::new(ValidationError { - problem: "`tiling` is `ImageTiling::DrmFormatModifier`, but \ - `flags` contains `ImageCreateFlags::MUTABLE_FORMAT`" + problem: "`tiling` is `ImageTiling::DrmFormatModifier`, and \ + `flags` contains `ImageCreateFlags::MUTABLE_FORMAT`, but \ + `view_formats` is empty" .into(), vuids: &["VUID-VkImageCreateInfo-tiling-02353"], ..Default::default() @@ -2649,6 +2744,39 @@ impl ImageCreateInfo { } } + if !external_memory_handle_types.is_empty() { + if !(device.api_version() >= Version::V1_1 + || device.enabled_extensions().khr_external_memory) + { + return Err(Box::new(ValidationError { + context: "external_memory_handle_types".into(), + problem: "is not empty".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]), + RequiresAllOf(&[Requires::DeviceExtension("khr_external_memory")]), + ]), + ..Default::default() + })); + } + + external_memory_handle_types + .validate_device(device) + .map_err(|err| { + err.add_context("external_memory_handle_types") + .set_vuids(&["VUID-VkExternalMemoryImageCreateInfo-handleTypes-parameter"]) + })?; + + if initial_layout != ImageLayout::Undefined { + return Err(Box::new(ValidationError { + problem: "`external_memory_handle_types` is not empty, but \ + `initial_layout` is not `ImageLayout::Undefined`" + .into(), + vuids: &["VUID-VkImageCreateInfo-pNext-01443"], + ..Default::default() + })); + } + } + /* Some device limits can be exceeded, but only for particular image configurations, which must be queried with `image_format_properties`. See: diff --git a/vulkano/src/image/view.rs b/vulkano/src/image/view.rs index c527b097ef..c0850f7afc 100644 --- a/vulkano/src/image/view.rs +++ b/vulkano/src/image/view.rs @@ -381,19 +381,6 @@ impl ImageView { /* Check flags requirements */ if format != image.format() { - if !image.format().planes().is_empty() - && subresource_range.aspects.intersects(ImageAspects::COLOR) - { - return Err(Box::new(ValidationError { - problem: "`image.format()` is a multi-planar format, and \ - `create_info.subresource_range.aspects` contains `ImageAspects::COLOR`, \ - but `create_info.format` does not equal `image.format()`" - .into(), - vuids: &["VUID-VkImageViewCreateInfo-image-01762"], - ..Default::default() - })); - } - if !image.flags().intersects(ImageCreateFlags::MUTABLE_FORMAT) { return Err(Box::new(ValidationError { problem: "`create_info.format` does not equal `image.format()`, but \ @@ -404,36 +391,42 @@ impl ImageView { })); } - // 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.format().components() + if !image.format().planes().is_empty() + && subresource_range.aspects.intersects(ImageAspects::COLOR) { return Err(Box::new(ValidationError { - problem: "this device is a portability subset device, and \ - `create_info.format` does not have the same components and \ - number of bits per component as `image.format()`" + problem: "`image.format()` is a multi-planar format, and \ + `create_info.subresource_range.aspects` contains `ImageAspects::COLOR`, \ + but `create_info.format` does not equal `image.format()`" .into(), - requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( - "image_view_format_reinterpretation", - )])]), - vuids: &["VUID-VkImageViewCreateInfo-imageViewFormatReinterpretation-04466"], + vuids: &["VUID-VkImageViewCreateInfo-image-01762"], ..Default::default() })); } - if image + if !image.view_formats().is_empty() { + if !image.view_formats().contains(&format) { + return Err(Box::new(ValidationError { + problem: "`image.view_formats()` is not empty, but it does not contain \ + `create_info.format`" + .into(), + vuids: &["VUID-VkImageViewCreateInfo-pNext-01585"], + ..Default::default() + })); + } + } else if image .flags() .intersects(ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE) + && format.compression().is_none() { if !(format.compatibility() == image.format().compatibility() || format.block_size() == image.format().block_size()) { return Err(Box::new(ValidationError { problem: "`image.flags()` contains \ - `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, but \ - `create_info.format` is not compatible with `image.format()`, or \ + `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, and \ + `create_info.format` is an uncompressed format, but \ + it is not compatible with `image.format()`, and \ does not have an equal block size" .into(), vuids: &["VUID-VkImageViewCreateInfo-image-01583"], @@ -441,41 +434,40 @@ impl ImageView { })); } - if format.compression().is_none() { - if subresource_range.array_layers.len() != 1 { - return Err(Box::new(ValidationError { - problem: "`image.flags()` contains \ - `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, and \ - `create_info.format` is not a compressed format, but \ - the length of `create_info.subresource_range.array_layers` \ - is not 1" - .into(), - vuids: &["VUID-VkImageViewCreateInfo-image-07072"], - ..Default::default() - })); - } + if subresource_range.array_layers.len() != 1 { + return Err(Box::new(ValidationError { + problem: "`image.flags()` contains \ + `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, and \ + `create_info.format` is an uncompressed format, but \ + the length of `create_info.subresource_range.array_layers` \ + is not 1" + .into(), + vuids: &["VUID-VkImageViewCreateInfo-image-07072"], + ..Default::default() + })); + } - if subresource_range.mip_levels.len() != 1 { - return Err(Box::new(ValidationError { - problem: "`image.flags()` contains \ - `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, and \ - `create_info.format` is not a compressed format, but \ - the length of `create_info.subresource_range.mip_levels` \ - is not 1" - .into(), - vuids: &["VUID-VkImageViewCreateInfo-image-07072"], - ..Default::default() - })); - } + if subresource_range.mip_levels.len() != 1 { + return Err(Box::new(ValidationError { + problem: "`image.flags()` contains \ + `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, and \ + `create_info.format` is an uncompressed format, but \ + the length of `create_info.subresource_range.mip_levels` \ + is not 1" + .into(), + vuids: &["VUID-VkImageViewCreateInfo-image-07072"], + ..Default::default() + })); } } else { if image.format().planes().is_empty() { if format.compatibility() != image.format().compatibility() { return Err(Box::new(ValidationError { problem: "`image.flags()` does not contain \ - `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, and \ + `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, or \ + `create_info.format` is a compressed format, and \ `image.format()` is not a multi-planar format, but \ - is not compatible with `create_info.format`" + it is not compatible with `create_info.format`" .into(), vuids: &["VUID-VkImageViewCreateInfo-image-01761"], ..Default::default() @@ -509,6 +501,25 @@ impl ImageView { } } } + + // 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.format().components() + { + return Err(Box::new(ValidationError { + problem: "this device is a portability subset device, and \ + `create_info.format` does not have the same components and \ + number of bits per component as `image.format()`" + .into(), + requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::Feature( + "image_view_format_reinterpretation", + )])]), + vuids: &["VUID-VkImageViewCreateInfo-imageViewFormatReinterpretation-04466"], + ..Default::default() + })); + } } if let Some(chroma_sampling) = format.ycbcr_chroma_sampling() { diff --git a/vulkano/src/swapchain/mod.rs b/vulkano/src/swapchain/mod.rs index efd1e9b3c5..dbba7536c4 100644 --- a/vulkano/src/swapchain/mod.rs +++ b/vulkano/src/swapchain/mod.rs @@ -334,11 +334,12 @@ mod surface; use crate::{ device::{Device, DeviceOwned}, format::Format, - image::{Image, ImageFormatInfo, ImageTiling, ImageType, ImageUsage}, + image::{Image, ImageCreateFlags, ImageFormatInfo, ImageTiling, ImageType, ImageUsage}, instance::InstanceOwnedDebugWrapper, macros::{impl_id_counter, vulkan_bitflags, vulkan_bitflags_enum, vulkan_enum}, sync::Sharing, - Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, VulkanError, VulkanObject, + Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError, + VulkanObject, }; use parking_lot::Mutex; use smallvec::SmallVec; @@ -364,6 +365,7 @@ pub struct Swapchain { flags: SwapchainCreateFlags, min_image_count: u32, image_format: Format, + image_view_formats: Vec, image_color_space: ColorSpace, image_extent: [u32; 2], image_array_layers: u32, @@ -538,6 +540,7 @@ impl Swapchain { flags: _, min_image_count, image_format, + image_view_formats: _, image_color_space, image_extent, image_array_layers, @@ -996,6 +999,7 @@ impl Swapchain { flags, min_image_count, image_format, + ref image_view_formats, image_color_space, image_extent, image_array_layers, @@ -1045,37 +1049,28 @@ impl Swapchain { old_swapchain: old_swapchain.map_or(ash::vk::SwapchainKHR::null(), |os| os.handle), ..Default::default() }; + let mut format_list_info_vk = None; + let format_list_view_formats_vk: Vec<_>; + let mut full_screen_exclusive_info_vk = None; + let mut full_screen_exclusive_win32_info_vk = None; let mut present_modes_info_vk = None; let present_modes_vk: SmallVec<[ash::vk::PresentModeKHR; PresentMode::COUNT]>; let mut present_scaling_info_vk = None; - let mut full_screen_exclusive_info_vk = None; - let mut full_screen_exclusive_win32_info_vk = None; - if !present_modes.is_empty() { - present_modes_vk = present_modes.iter().copied().map(Into::into).collect(); + if !image_view_formats.is_empty() { + format_list_view_formats_vk = image_view_formats + .iter() + .copied() + .map(ash::vk::Format::from) + .collect(); - let next = present_modes_info_vk.insert(ash::vk::SwapchainPresentModesCreateInfoEXT { - present_mode_count: present_modes_vk.len() as u32, - p_present_modes: present_modes_vk.as_ptr(), + let next = format_list_info_vk.insert(ash::vk::ImageFormatListCreateInfo { + view_format_count: format_list_view_formats_vk.len() as u32, + p_view_formats: format_list_view_formats_vk.as_ptr(), ..Default::default() }); - next.p_next = create_info_vk.p_next as *mut _; - create_info_vk.p_next = next as *const _ as *const _; - } - - if scaling_behavior.is_some() || present_gravity.is_some() { - let [present_gravity_x, present_gravity_y] = - present_gravity.map_or_else(Default::default, |pg| pg.map(Into::into)); - let next = - present_scaling_info_vk.insert(ash::vk::SwapchainPresentScalingCreateInfoEXT { - scaling_behavior: scaling_behavior.map_or_else(Default::default, Into::into), - present_gravity_x, - present_gravity_y, - ..Default::default() - }); - - next.p_next = create_info_vk.p_next as *mut _; + next.p_next = create_info_vk.p_next; create_info_vk.p_next = next as *const _ as *const _; } @@ -1102,6 +1097,34 @@ impl Swapchain { create_info_vk.p_next = next as *const _ as *const _; } + if !present_modes.is_empty() { + present_modes_vk = present_modes.iter().copied().map(Into::into).collect(); + + let next = present_modes_info_vk.insert(ash::vk::SwapchainPresentModesCreateInfoEXT { + present_mode_count: present_modes_vk.len() as u32, + p_present_modes: present_modes_vk.as_ptr(), + ..Default::default() + }); + + next.p_next = create_info_vk.p_next as *mut _; + create_info_vk.p_next = next as *const _ as *const _; + } + + if scaling_behavior.is_some() || present_gravity.is_some() { + let [present_gravity_x, present_gravity_y] = + present_gravity.map_or_else(Default::default, |pg| pg.map(Into::into)); + let next = + present_scaling_info_vk.insert(ash::vk::SwapchainPresentScalingCreateInfoEXT { + scaling_behavior: scaling_behavior.map_or_else(Default::default, Into::into), + present_gravity_x, + present_gravity_y, + ..Default::default() + }); + + next.p_next = create_info_vk.p_next as *mut _; + create_info_vk.p_next = next as *const _ as *const _; + } + let fns = device.fns(); let handle = { @@ -1169,6 +1192,7 @@ impl Swapchain { flags, min_image_count, image_format, + image_view_formats, image_color_space, image_extent, image_array_layers, @@ -1195,6 +1219,7 @@ impl Swapchain { flags, min_image_count, image_format, + image_view_formats, image_color_space, image_extent, image_array_layers, @@ -1245,6 +1270,7 @@ impl Swapchain { flags: self.flags, min_image_count: self.min_image_count, image_format: self.image_format, + image_view_formats: self.image_view_formats.clone(), image_color_space: self.image_color_space, image_extent: self.image_extent, image_array_layers: self.image_array_layers, @@ -1296,6 +1322,12 @@ impl Swapchain { self.image_format } + /// Returns the formats that an image view created from a swapchain image can have. + #[inline] + pub fn image_view_formats(&self) -> &[Format] { + &self.image_view_formats + } + /// Returns the color space of the images of the swapchain. #[inline] pub fn image_color_space(&self) -> ColorSpace { @@ -1583,6 +1615,25 @@ pub struct SwapchainCreateInfo { /// The default value is `Format::UNDEFINED`. pub image_format: Format, + /// The additional formats that an image view can have when it is created from one of the + /// swapchain images. + /// + /// If the list is not empty, and `flags` does not contain + /// [`SwapchainCreateFlags::MUTABLE_FORMAT`], then the list must contain at most one element, + /// otherwise any number of elements are allowed. + /// The view formats must be compatible with `format`. + /// + /// If `flags` contains [`SwapchainCreateFlags::MUTABLE_FORMAT`], then the list must contain + /// `image_format. + /// + /// If this is not empty, then the device API version must be at least 1.2, or the + /// [`khr_image_format_list`] extension must be enabled on the device. + /// + /// The default value is empty. + /// + /// [`khr_image_format_list`]: crate::device::DeviceExtensions::khr_image_format_list + pub image_view_formats: Vec, + /// The color space of the created images. /// /// The default value is [`ColorSpace::SrgbNonLinear`]. @@ -1699,6 +1750,7 @@ impl Default for SwapchainCreateInfo { flags: SwapchainCreateFlags::empty(), min_image_count: 2, image_format: Format::UNDEFINED, + image_view_formats: Vec::new(), image_color_space: ColorSpace::SrgbNonLinear, image_extent: [0, 0], image_array_layers: 1, @@ -1724,6 +1776,7 @@ impl SwapchainCreateInfo { flags, min_image_count: _, image_format, + ref image_view_formats, image_color_space, image_extent, image_array_layers, @@ -1879,6 +1932,65 @@ impl SwapchainCreateInfo { })); } + if flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT) + && !image_view_formats.contains(&image_format) + { + return Err(Box::new(ValidationError { + problem: "`flags` contains `SwapchainCreateFlags::MUTABLE_FORMAT`, but \ + `image_view_formats` does not contain `image_format`" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-03168"], + ..Default::default() + })); + } + + if !image_view_formats.is_empty() { + if !(device.api_version() >= Version::V1_2 + || device.enabled_extensions().khr_image_format_list) + { + return Err(Box::new(ValidationError { + context: "image_view_formats".into(), + problem: "is not empty".into(), + requires_one_of: RequiresOneOf(&[ + RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]), + RequiresAllOf(&[Requires::DeviceExtension("khr_image_format_list")]), + ]), + ..Default::default() + })); + } + + if !flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT) + && image_view_formats.len() != 1 + { + return Err(Box::new(ValidationError { + problem: "`flags` does not contain `SwapchainCreateFlags::MUTABLE_FORMAT`, \ + but `image_view_formats` contains more than one element" + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-flags-04100"], + ..Default::default() + })); + } + + for (index, &image_view_format) in image_view_formats.iter().enumerate() { + image_view_format.validate_device(device).map_err(|err| { + err.add_context(format!("image_view_formats[{}]", index)) + .set_vuids(&["VUID-VkImageFormatListCreateInfo-pViewFormats-parameter"]) + })?; + + if image_view_format.compatibility() != image_format.compatibility() { + return Err(Box::new(ValidationError { + problem: format!( + "`image_view_formats[{}]` is not compatible with `image_format`", + index + ) + .into(), + vuids: &["VUID-VkSwapchainCreateInfoKHR-pNext-04099"], + ..Default::default() + })); + } + } + } + if !present_modes.is_empty() { if !device.enabled_extensions().ext_swapchain_maintenance1 { return Err(Box::new(ValidationError { @@ -1995,22 +2107,26 @@ vulkan_bitflags! { SwapchainCreateFlags = SwapchainCreateFlagsKHR(u32); /* TODO: enable - // TODO: document - SPLIT_INSTANCE_BIND_REGIONS = SPLIT_INSTANCE_BIND_REGIONS { - // Provided by VK_VERSION_1_1 with VK_KHR_swapchain, VK_KHR_device_group with VK_KHR_swapchain - },*/ + /// Creates swapchain images with the [`ImageCreateFlags::SPLIT_INSTANCE_BIND_REGIONS`] flag. + SPLIT_INSTANCE_BIND_REGIONS = SPLIT_INSTANCE_BIND_REGIONS + RequiresOneOf([ + RequiresAllOf([APIVersion(V1_1)]), + RequiresAllOf([DeviceExtension(khr_device_group)]), + ]),*/ /* TODO: enable - // TODO: document - PROTECTED = PROTECTED { - // Provided by VK_VERSION_1_1 with VK_KHR_swapchain - },*/ - - /* TODO: enable - // TODO: document - MUTABLE_FORMAT = MUTABLE_FORMAT { - device_extensions: [khr_swapchain_mutable_format], - },*/ + /// Creates swapchain images with the [`ImageCreateFlags::PROTECTED`] flag. + PROTECTED = PROTECTED + RequiresOneOf([ + RequiresAllOf([APIVersion(V1_1)]), + ]),*/ + + /// Creates swapchain images with both the [`ImageCreateFlags::MUTABLE_FORMAT`] and + /// [`ImageCreateFlags::EXTENDED_USAGE`] flags. + MUTABLE_FORMAT = MUTABLE_FORMAT + RequiresOneOf([ + RequiresAllOf([DeviceExtension(khr_swapchain_mutable_format)]), + ]), /* TODO: enable // TODO: document @@ -2019,6 +2135,30 @@ vulkan_bitflags! { },*/ } +impl From for ImageCreateFlags { + #[inline] + fn from(flags: SwapchainCreateFlags) -> Self { + // Per https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/vkCreateSwapchainKHR.html#_description + let mut result = ImageCreateFlags::empty(); + + /* TODO: enable + if flags.intersects(SwapchainCreateFlags::SPLIT_INSTANCE_BIND_REGIONS) { + result |= ImageCreateFlags::SPLIT_INSTANCE_BIND_REGIONS; + } */ + + /* TODO: enable + if flags.intersects(SwapchainCreateFlags::PROTECTED) { + result |= ImageCreateFlags::PROTECTED; + } */ + + if flags.intersects(SwapchainCreateFlags::MUTABLE_FORMAT) { + result |= ImageCreateFlags::MUTABLE_FORMAT | ImageCreateFlags::EXTENDED_USAGE; + } + + result + } +} + vulkan_bitflags_enum! { #[non_exhaustive] From 26930a465a6002c24bc7548250ab2eb6f6ee99e1 Mon Sep 17 00:00:00 2001 From: Rua Date: Mon, 14 Aug 2023 12:25:24 +0200 Subject: [PATCH 2/2] Not "additional" formats --- vulkano/src/image/sys.rs | 2 +- vulkano/src/swapchain/mod.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/vulkano/src/image/sys.rs b/vulkano/src/image/sys.rs index 7e3e38df8f..ddffdaf5cb 100644 --- a/vulkano/src/image/sys.rs +++ b/vulkano/src/image/sys.rs @@ -1652,7 +1652,7 @@ pub struct ImageCreateInfo { /// The default value is `Format::UNDEFINED`. pub format: Format, - /// The additional formats that an image view can have when it is created from this image. + /// The formats that an image view can have when it is created from this image. /// /// If the list is not empty, and `flags` does not contain [`ImageCreateFlags::MUTABLE_FORMAT`], /// then the list must contain at most one element, otherwise any number of elements are diff --git a/vulkano/src/swapchain/mod.rs b/vulkano/src/swapchain/mod.rs index dbba7536c4..7e1f58251e 100644 --- a/vulkano/src/swapchain/mod.rs +++ b/vulkano/src/swapchain/mod.rs @@ -1615,8 +1615,7 @@ pub struct SwapchainCreateInfo { /// The default value is `Format::UNDEFINED`. pub image_format: Format, - /// The additional formats that an image view can have when it is created from one of the - /// swapchain images. + /// The formats that an image view can have when it is created from one of the swapchain images. /// /// If the list is not empty, and `flags` does not contain /// [`SwapchainCreateFlags::MUTABLE_FORMAT`], then the list must contain at most one element,