Add support for image format lists (#2287)

* Add support for image format lists

* Not "additional" formats
This commit is contained in:
Rua 2023-08-24 00:44:47 +02:00 committed by GitHub
parent b3ef5d746b
commit d47f45c478
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 587 additions and 240 deletions

View File

@ -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 {

View File

@ -207,10 +207,12 @@ impl Image {
swapchain: Arc<Swapchain>,
image_index: u32,
) -> Result<Self, VulkanError> {
// 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<Format>,
/// 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<ImageUsage>,
/// 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<ImageDrmFormatModifierInfo>,
/// 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<ImageViewType>,
/// 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<ImageDrmFormatModifierInfo>,
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,6 +1860,80 @@ impl ImageFormatInfo {
}
}
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 {
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()
}));
}
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"])
})?;
}
}
if let Some(drm_format_modifier_info) = drm_format_modifier_info {
if !physical_device
.supported_extensions()
.ext_image_drm_format_modifier
{
return Err(Box::new(ValidationError {
context: "drm_format_modifier_info".into(),
problem: "is `Some`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_image_drm_format_modifier",
)])]),
..Default::default()
}));
}
drm_format_modifier_info
.validate(physical_device)
.map_err(|err| err.add_context("drm_format_modifier_info"))?;
if tiling != ImageTiling::DrmFormatModifier {
return Err(Box::new(ValidationError {
problem: "`drm_format_modifier_info` is `Some` but \
`tiling` is not `ImageTiling::DrmFormatModifier`"
.into(),
vuids: &[" VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02249"],
..Default::default()
}));
}
if flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) && view_formats.is_empty() {
return Err(Box::new(ValidationError {
problem: "`tiling` is `ImageTiling::DrmFormatModifier`, and \
`flags` contains `ImageCreateFlags::MUTABLE_FORMAT`, but \
`view_formats` is empty"
.into(),
vuids: &["VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02313"],
..Default::default()
}));
}
} else if tiling == ImageTiling::DrmFormatModifier {
return Err(Box::new(ValidationError {
problem: "`tiling` is `ImageTiling::DrmFormatModifier`, but \
`drm_format_modifier_info` is `None`"
.into(),
vuids: &[" VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02249"],
..Default::default()
}));
}
if let Some(handle_type) = external_memory_handle_type {
if !(physical_device.api_version() >= Version::V1_1
|| physical_device
@ -1885,54 +1982,6 @@ impl ImageFormatInfo {
})?;
}
if let Some(drm_format_modifier_info) = drm_format_modifier_info {
if !physical_device
.supported_extensions()
.ext_image_drm_format_modifier
{
return Err(Box::new(ValidationError {
context: "drm_format_modifier_info".into(),
problem: "is `Some`".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_image_drm_format_modifier",
)])]),
..Default::default()
}));
}
drm_format_modifier_info
.validate(physical_device)
.map_err(|err| err.add_context("drm_format_modifier_info"))?;
if tiling != ImageTiling::DrmFormatModifier {
return Err(Box::new(ValidationError {
problem: "`drm_format_modifier_info` is `Some` but \
`tiling` is not `ImageTiling::DrmFormatModifier`"
.into(),
vuids: &[" VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02249"],
..Default::default()
}));
}
if flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) {
return Err(Box::new(ValidationError {
problem: "`tiling` is `ImageTiling::DrmFormatModifier`, but \
`flags` contains `ImageCreateFlags::MUTABLE_FORMAT`"
.into(),
vuids: &["VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02313"],
..Default::default()
}));
}
} else if tiling == ImageTiling::DrmFormatModifier {
return Err(Box::new(ValidationError {
problem: "`tiling` is `ImageTiling::DrmFormatModifier`, but \
`drm_format_modifier_info` is `None`"
.into(),
vuids: &[" VUID-VkPhysicalDeviceImageFormatInfo2-tiling-02249"],
..Default::default()
}));
}
Ok(())
}
}

View File

@ -63,6 +63,7 @@ pub struct RawImage {
image_type: ImageType,
format: Format,
format_features: FormatFeatures,
view_formats: Vec<Format>,
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(()),
})
}
@ -1222,6 +1251,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] {
@ -1626,9 +1661,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.
@ -1641,6 +1676,27 @@ pub struct ImageCreateInfo {
/// The default value is `Format::UNDEFINED`.
pub format: Format,
/// 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
/// 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<Format>,
/// The width, height and depth of the image.
///
/// If `image_type` is `ImageType::Dim2d`, then the depth must be 1.
@ -1681,7 +1737,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
@ -1704,16 +1760,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
@ -1721,7 +1767,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<u64>,
/// If `drm_format_modifiers` contains one element, provides the subresource layouts of each
/// memory plane of the image. The number of elements must equal
@ -1732,11 +1778,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<SubresourceLayout>,
/// 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,
}
@ -1748,6 +1805,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,
@ -1758,8 +1816,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(()),
}
}
@ -1771,6 +1829,7 @@ impl ImageCreateInfo {
flags,
image_type,
format,
ref view_formats,
extent,
array_layers,
mip_levels,
@ -1780,10 +1839,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();
@ -1875,6 +1934,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 {
@ -2431,7 +2560,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()
@ -2442,7 +2571,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()
@ -2506,41 +2635,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.
@ -2554,10 +2648,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()
@ -2673,6 +2768,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:

View File

@ -381,6 +381,16 @@ impl ImageView {
/* Check flags requirements */
if format != image.format() {
if !image.flags().intersects(ImageCreateFlags::MUTABLE_FORMAT) {
return Err(Box::new(ValidationError {
problem: "`create_info.format` does not equal `image.format()`, but \
`image.flags()` does not contain `ImageCreateFlags::MUTABLE_FORMAT`"
.into(),
vuids: &["VUID-VkImageViewCreateInfo-image-01762"],
..Default::default()
}));
}
if !image.format().planes().is_empty()
&& subresource_range.aspects.intersects(ImageAspects::COLOR)
{
@ -394,46 +404,29 @@ impl ImageView {
}));
}
if !image.flags().intersects(ImageCreateFlags::MUTABLE_FORMAT) {
return Err(Box::new(ValidationError {
problem: "`create_info.format` does not equal `image.format()`, but \
`image.flags()` does not contain `ImageCreateFlags::MUTABLE_FORMAT`"
.into(),
vuids: &["VUID-VkImageViewCreateInfo-image-01762"],
..Default::default()
}));
}
// 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 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() {

View File

@ -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<Format>,
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,11 +1049,53 @@ 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 !image_view_formats.is_empty() {
format_list_view_formats_vk = image_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 full_screen_exclusive != FullScreenExclusive::Default {
let next =
full_screen_exclusive_info_vk.insert(ash::vk::SurfaceFullScreenExclusiveInfoEXT {
full_screen_exclusive: full_screen_exclusive.into(),
..Default::default()
});
next.p_next = create_info_vk.p_next as *mut _;
create_info_vk.p_next = next as *const _ as *const _;
}
if let Some(Win32Monitor(hmonitor)) = win32_monitor {
let next = full_screen_exclusive_win32_info_vk.insert(
ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT {
hmonitor,
..Default::default()
},
);
next.p_next = create_info_vk.p_next as *mut _;
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();
@ -1079,29 +1125,6 @@ impl Swapchain {
create_info_vk.p_next = next as *const _ as *const _;
}
if full_screen_exclusive != FullScreenExclusive::Default {
let next =
full_screen_exclusive_info_vk.insert(ash::vk::SurfaceFullScreenExclusiveInfoEXT {
full_screen_exclusive: full_screen_exclusive.into(),
..Default::default()
});
next.p_next = create_info_vk.p_next as *mut _;
create_info_vk.p_next = next as *const _ as *const _;
}
if let Some(Win32Monitor(hmonitor)) = win32_monitor {
let next = full_screen_exclusive_win32_info_vk.insert(
ash::vk::SurfaceFullScreenExclusiveWin32InfoEXT {
hmonitor,
..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,24 @@ pub struct SwapchainCreateInfo {
/// The default value is `Format::UNDEFINED`.
pub image_format: Format,
/// 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,
/// 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<Format>,
/// The color space of the created images.
///
/// The default value is [`ColorSpace::SrgbNonLinear`].
@ -1699,6 +1749,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 +1775,7 @@ impl SwapchainCreateInfo {
flags,
min_image_count: _,
image_format,
ref image_view_formats,
image_color_space,
image_extent,
image_array_layers,
@ -1879,6 +1931,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 +2106,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
},*/
/// Creates swapchain images with the [`ImageCreateFlags::PROTECTED`] flag.
PROTECTED = PROTECTED
RequiresOneOf([
RequiresAllOf([APIVersion(V1_1)]),
]),*/
/* TODO: enable
// TODO: document
MUTABLE_FORMAT = MUTABLE_FORMAT {
device_extensions: [khr_swapchain_mutable_format],
},*/
/// 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 +2134,30 @@ vulkan_bitflags! {
},*/
}
impl From<SwapchainCreateFlags> 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]