vulkano/image/
sys.rs

1//! Low-level implementation of images.
2//!
3//! This module contains low-level wrappers around the Vulkan image types. All
4//! other image types of this library, and all custom image types
5//! that you create must wrap around the types in this module.
6//!
7//! See also [the parent module-level documentation] for more information about images.
8//!
9//! [the parent module-level documentation]: super
10
11use super::{
12    Image, ImageAspect, ImageAspects, ImageCreateFlags, ImageLayout, ImageMemory,
13    ImageSubresourceLayers, ImageSubresourceRange, ImageTiling, ImageUsage, SampleCount,
14    SparseImageMemoryRequirements, SubresourceLayout,
15};
16#[cfg(doc)]
17use crate::format::DrmFormatModifierProperties;
18use crate::{
19    cache::OnceCache,
20    device::{Device, DeviceOwned},
21    format::{ChromaSampling, Format, FormatFeatures},
22    image::{
23        max_mip_levels, ImageDrmFormatModifierInfo, ImageFormatInfo, ImageFormatProperties,
24        ImageType,
25    },
26    instance::InstanceOwnedDebugWrapper,
27    macros::impl_id_counter,
28    memory::{
29        allocator::AllocationType, is_aligned, DedicatedTo, ExternalMemoryHandleTypes,
30        MemoryPropertyFlags, MemoryRequirements, ResourceMemory,
31    },
32    sync::Sharing,
33    Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError, Version, VulkanError,
34    VulkanObject,
35};
36use smallvec::{smallvec, SmallVec};
37use std::{marker::PhantomData, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc};
38
39/// A raw image, with no memory backing it.
40///
41/// This is the basic image type, a direct translation of a `VkImage` object, but it is mostly
42/// useless in this form. After creating a raw image, you must call `bind_memory` to make a
43/// complete image object.
44///
45/// See also [the parent module-level documentation] for more information about images.
46///
47/// [the parent module-level documentation]: super
48#[derive(Debug)]
49pub struct RawImage {
50    handle: ash::vk::Image,
51    device: InstanceOwnedDebugWrapper<Arc<Device>>,
52    id: NonZeroU64,
53
54    flags: ImageCreateFlags,
55    image_type: ImageType,
56    format: Format,
57    format_features: FormatFeatures,
58    view_formats: Vec<Format>,
59    extent: [u32; 3],
60    array_layers: u32,
61    mip_levels: u32,
62    samples: SampleCount,
63    tiling: ImageTiling,
64    usage: ImageUsage,
65    stencil_usage: Option<ImageUsage>,
66    sharing: Sharing<SmallVec<[u32; 4]>>,
67    initial_layout: ImageLayout,
68    drm_format_modifier: Option<(u64, u32)>,
69    external_memory_handle_types: ExternalMemoryHandleTypes,
70
71    memory_requirements: SmallVec<[MemoryRequirements; 4]>,
72    sparse_memory_requirements: Vec<SparseImageMemoryRequirements>,
73    needs_destruction: bool, // `vkDestroyImage` is called only if true.
74    subresource_layout: OnceCache<(ImageAspect, u32, u32), SubresourceLayout>,
75}
76
77impl RawImage {
78    /// Creates a new `RawImage`.
79    #[inline]
80    pub fn new(
81        device: Arc<Device>,
82        create_info: ImageCreateInfo,
83    ) -> Result<RawImage, Validated<VulkanError>> {
84        Self::validate_new(&device, &create_info)?;
85
86        Ok(unsafe { RawImage::new_unchecked(device, create_info) }?)
87    }
88
89    fn validate_new(
90        device: &Device,
91        create_info: &ImageCreateInfo,
92    ) -> Result<(), Box<ValidationError>> {
93        create_info
94            .validate(device)
95            .map_err(|err| err.add_context("create_info"))?;
96
97        // TODO: sparse_address_space_size and extended_sparse_address_space_size limits
98        // VUID-vkCreateImage-flags-00939
99        // VUID-vkCreateImage-flags-09385
100        // VUID-vkCreateImage-flags-09386
101
102        Ok(())
103    }
104
105    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
106    #[inline]
107    pub unsafe fn new_unchecked(
108        device: Arc<Device>,
109        create_info: ImageCreateInfo,
110    ) -> Result<Self, VulkanError> {
111        let create_info_fields1_vk = create_info.to_vk_fields1();
112        let mut create_info_extensions_vk = create_info.to_vk_extensions(&create_info_fields1_vk);
113        let create_info_vk = create_info.to_vk(&mut create_info_extensions_vk);
114
115        let handle = {
116            let fns = device.fns();
117            let mut output = MaybeUninit::uninit();
118            unsafe {
119                (fns.v1_0.create_image)(
120                    device.handle(),
121                    &create_info_vk,
122                    ptr::null(),
123                    output.as_mut_ptr(),
124                )
125            }
126            .result()
127            .map_err(VulkanError::from)?;
128            unsafe { output.assume_init() }
129        };
130
131        unsafe { Self::from_handle(device, handle, create_info) }
132    }
133
134    /// Creates a new `RawImage` from a raw object handle.
135    ///
136    /// # Safety
137    ///
138    /// - `handle` must be a valid Vulkan object handle created from `device`.
139    /// - `create_info` must match the info used to create the object.
140    /// - If the image has memory bound to it, `bind_memory` must not be called on the returned
141    ///   `RawImage`.
142    #[inline]
143    pub unsafe fn from_handle(
144        device: Arc<Device>,
145        handle: ash::vk::Image,
146        create_info: ImageCreateInfo,
147    ) -> Result<Self, VulkanError> {
148        unsafe { Self::from_handle_with_destruction(device, handle, create_info, true) }
149    }
150
151    /// Creates a new `RawImage` from a raw object handle. Unlike `from_handle`, the created
152    /// `RawImage` will not destroy the inner image when dropped.
153    ///
154    /// # Safety
155    ///
156    /// - `handle` must be a valid Vulkan object handle created from `device`.
157    /// - `create_info` must match the info used to create the object.
158    /// - If the image has memory bound to it, `bind_memory` must not be called on the returned
159    ///   `RawImage`.
160    /// - Caller must ensure the handle will not be destroyed for the lifetime of returned
161    ///   `RawImage`.
162    #[inline]
163    pub unsafe fn from_handle_borrowed(
164        device: Arc<Device>,
165        handle: ash::vk::Image,
166        create_info: ImageCreateInfo,
167    ) -> Result<Self, VulkanError> {
168        unsafe { Self::from_handle_with_destruction(device, handle, create_info, false) }
169    }
170
171    pub(super) unsafe fn from_handle_with_destruction(
172        device: Arc<Device>,
173        handle: ash::vk::Image,
174        create_info: ImageCreateInfo,
175        needs_destruction: bool,
176    ) -> Result<Self, VulkanError> {
177        let ImageCreateInfo {
178            flags,
179            image_type,
180            format,
181            view_formats,
182            extent,
183            array_layers,
184            mip_levels,
185            samples,
186            tiling,
187            usage,
188            stencil_usage,
189            sharing,
190            initial_layout,
191            drm_format_modifiers: _,
192            drm_format_modifier_plane_layouts: _,
193            external_memory_handle_types,
194            _ne: _,
195        } = create_info;
196
197        let format_properties =
198            unsafe { device.physical_device().format_properties_unchecked(format) };
199
200        let drm_format_modifier = if tiling == ImageTiling::DrmFormatModifier {
201            let drm_format_modifier =
202                unsafe { Self::get_drm_format_modifier_properties(&device, handle) }?;
203            let drm_format_modifier_plane_count = format_properties
204                .drm_format_modifier_properties
205                .iter()
206                .find(|properties| properties.drm_format_modifier == drm_format_modifier)
207                .expect("couldn't get the DRM format modifier plane count for the image")
208                .drm_format_modifier_plane_count;
209            Some((drm_format_modifier, drm_format_modifier_plane_count))
210        } else {
211            None
212        };
213
214        let format_features = {
215            let drm_format_modifiers: SmallVec<[_; 1]> =
216                drm_format_modifier.map_or_else(Default::default, |(m, _)| smallvec![m]);
217            format_properties.format_features(tiling, &drm_format_modifiers)
218        };
219
220        let memory_requirements = if needs_destruction {
221            if flags.intersects(ImageCreateFlags::DISJOINT) {
222                // VUID-VkImageMemoryRequirementsInfo2-image-01589
223                // VUID-VkImageMemoryRequirementsInfo2-image-02279
224                let plane_count = drm_format_modifier.map_or_else(
225                    || format.planes().len(),
226                    |(_, plane_count)| plane_count as usize,
227                );
228
229                (0..plane_count)
230                    .map(|plane| unsafe {
231                        Self::get_memory_requirements(&device, handle, Some((plane, tiling)))
232                    })
233                    .collect()
234            } else {
235                // VUID-VkImageMemoryRequirementsInfo2-image-01590
236                smallvec![unsafe { Self::get_memory_requirements(&device, handle, None) }]
237            }
238        } else {
239            smallvec![]
240        };
241
242        let sparse_memory_requirements = if flags
243            .contains(ImageCreateFlags::SPARSE_BINDING | ImageCreateFlags::SPARSE_RESIDENCY)
244        {
245            unsafe { Self::get_sparse_memory_requirements(&device, handle) }
246        } else {
247            Vec::new()
248        };
249
250        Ok(RawImage {
251            handle,
252            device: InstanceOwnedDebugWrapper(device),
253            id: Self::next_id(),
254
255            flags,
256            image_type,
257            format,
258            format_features,
259            view_formats,
260            extent,
261            array_layers,
262            mip_levels,
263            initial_layout,
264            samples,
265            tiling,
266            usage,
267            stencil_usage,
268            sharing,
269            drm_format_modifier,
270            external_memory_handle_types,
271
272            memory_requirements,
273            sparse_memory_requirements,
274            needs_destruction,
275            subresource_layout: OnceCache::new(),
276        })
277    }
278
279    unsafe fn get_memory_requirements(
280        device: &Device,
281        handle: ash::vk::Image,
282        plane_tiling: Option<(usize, ImageTiling)>,
283    ) -> MemoryRequirements {
284        let mut info_vk = ash::vk::ImageMemoryRequirementsInfo2::default().image(handle);
285        let mut plane_info_vk = None;
286
287        if let Some((plane, tiling)) = plane_tiling {
288            debug_assert!(
289                device.api_version() >= Version::V1_1
290                    || device.enabled_extensions().khr_get_memory_requirements2
291            );
292
293            let plane_aspect = match tiling {
294                // VUID-VkImagePlaneMemoryRequirementsInfo-planeAspect-02281
295                ImageTiling::Optimal | ImageTiling::Linear => {
296                    debug_assert!(
297                        device.api_version() >= Version::V1_1
298                            || device.enabled_extensions().khr_sampler_ycbcr_conversion
299                    );
300                    match plane {
301                        0 => ash::vk::ImageAspectFlags::PLANE_0,
302                        1 => ash::vk::ImageAspectFlags::PLANE_1,
303                        2 => ash::vk::ImageAspectFlags::PLANE_2,
304                        _ => unreachable!(),
305                    }
306                }
307                // VUID-VkImagePlaneMemoryRequirementsInfo-planeAspect-02282
308                ImageTiling::DrmFormatModifier => {
309                    debug_assert!(device.enabled_extensions().ext_image_drm_format_modifier);
310                    match plane {
311                        0 => ash::vk::ImageAspectFlags::MEMORY_PLANE_0_EXT,
312                        1 => ash::vk::ImageAspectFlags::MEMORY_PLANE_1_EXT,
313                        2 => ash::vk::ImageAspectFlags::MEMORY_PLANE_2_EXT,
314                        3 => ash::vk::ImageAspectFlags::MEMORY_PLANE_3_EXT,
315                        _ => unreachable!(),
316                    }
317                }
318            };
319
320            let next = plane_info_vk.insert(
321                ash::vk::ImagePlaneMemoryRequirementsInfo::default().plane_aspect(plane_aspect),
322            );
323            info_vk = info_vk.push_next(next);
324        }
325
326        let mut memory_requirements2_extensions_vk =
327            MemoryRequirements::to_mut_vk2_extensions(device);
328        let mut memory_requirements2_vk =
329            MemoryRequirements::to_mut_vk2(&mut memory_requirements2_extensions_vk);
330
331        let fns = device.fns();
332
333        if device.api_version() >= Version::V1_1
334            || device.enabled_extensions().khr_get_memory_requirements2
335        {
336            if device.api_version() >= Version::V1_1 {
337                unsafe {
338                    (fns.v1_1.get_image_memory_requirements2)(
339                        device.handle(),
340                        &info_vk,
341                        &mut memory_requirements2_vk,
342                    )
343                };
344            } else {
345                unsafe {
346                    (fns.khr_get_memory_requirements2
347                        .get_image_memory_requirements2_khr)(
348                        device.handle(),
349                        &info_vk,
350                        &mut memory_requirements2_vk,
351                    )
352                };
353            }
354        } else {
355            unsafe {
356                (fns.v1_0.get_image_memory_requirements)(
357                    device.handle(),
358                    handle,
359                    &mut memory_requirements2_vk.memory_requirements,
360                )
361            };
362        }
363
364        // Unborrow
365        let memory_requirements2_vk = ash::vk::MemoryRequirements2 {
366            _marker: PhantomData,
367            ..memory_requirements2_vk
368        };
369
370        MemoryRequirements::from_vk2(
371            &memory_requirements2_vk,
372            &memory_requirements2_extensions_vk,
373        )
374    }
375
376    unsafe fn get_sparse_memory_requirements(
377        device: &Device,
378        handle: ash::vk::Image,
379    ) -> Vec<SparseImageMemoryRequirements> {
380        let fns = device.fns();
381
382        if device.api_version() >= Version::V1_1
383            || device.enabled_extensions().khr_get_memory_requirements2
384        {
385            let info2_vk = ash::vk::ImageSparseMemoryRequirementsInfo2::default().image(handle);
386
387            let mut count = 0;
388
389            if device.api_version() >= Version::V1_1 {
390                unsafe {
391                    (fns.v1_1.get_image_sparse_memory_requirements2)(
392                        device.handle(),
393                        &info2_vk,
394                        &mut count,
395                        ptr::null_mut(),
396                    )
397                };
398            } else {
399                unsafe {
400                    (fns.khr_get_memory_requirements2
401                        .get_image_sparse_memory_requirements2_khr)(
402                        device.handle(),
403                        &info2_vk,
404                        &mut count,
405                        ptr::null_mut(),
406                    )
407                };
408            }
409
410            let mut requirements2_vk =
411                vec![SparseImageMemoryRequirements::to_mut_vk2(); count as usize];
412
413            if device.api_version() >= Version::V1_1 {
414                unsafe {
415                    (fns.v1_1.get_image_sparse_memory_requirements2)(
416                        device.handle(),
417                        &info2_vk,
418                        &mut count,
419                        requirements2_vk.as_mut_ptr(),
420                    )
421                };
422            } else {
423                unsafe {
424                    (fns.khr_get_memory_requirements2
425                        .get_image_sparse_memory_requirements2_khr)(
426                        device.handle(),
427                        &info2_vk,
428                        &mut count,
429                        requirements2_vk.as_mut_ptr(),
430                    )
431                };
432            }
433
434            unsafe { requirements2_vk.set_len(count as usize) };
435            requirements2_vk
436                .iter()
437                .map(SparseImageMemoryRequirements::from_vk2)
438                .collect()
439        } else {
440            let mut count = 0;
441
442            unsafe {
443                (fns.v1_0.get_image_sparse_memory_requirements)(
444                    device.handle(),
445                    handle,
446                    &mut count,
447                    ptr::null_mut(),
448                )
449            };
450
451            let mut requirements_vk =
452                vec![SparseImageMemoryRequirements::to_mut_vk(); count as usize];
453
454            unsafe {
455                (fns.v1_0.get_image_sparse_memory_requirements)(
456                    device.handle(),
457                    handle,
458                    &mut count,
459                    requirements_vk.as_mut_ptr(),
460                )
461            };
462
463            unsafe { requirements_vk.set_len(count as usize) };
464            requirements_vk
465                .iter()
466                .map(SparseImageMemoryRequirements::from_vk)
467                .collect()
468        }
469    }
470
471    unsafe fn get_drm_format_modifier_properties(
472        device: &Device,
473        handle: ash::vk::Image,
474    ) -> Result<u64, VulkanError> {
475        let mut properties_vk = ash::vk::ImageDrmFormatModifierPropertiesEXT::default();
476
477        let fns = device.fns();
478        unsafe {
479            (fns.ext_image_drm_format_modifier
480                .get_image_drm_format_modifier_properties_ext)(
481                device.handle(),
482                handle,
483                &mut properties_vk,
484            )
485        }
486        .result()
487        .map_err(VulkanError::from)?;
488
489        Ok(properties_vk.drm_format_modifier)
490    }
491
492    /// Binds device memory to this image.
493    ///
494    /// - If `self.flags()` does not contain `ImageCreateFlags::DISJOINT`, then `allocations` must
495    ///   contain exactly one element.
496    /// - If `self.flags()` contains `ImageCreateFlags::DISJOINT`, and `self.tiling()` is
497    ///   `ImageTiling::Linear` or `ImageTiling::Optimal`, then `allocations` must contain exactly
498    ///   `self.format().unwrap().planes().len()` elements.
499    /// - If `self.flags()` contains `ImageCreateFlags::DISJOINT`, and `self.tiling()` is
500    ///   `ImageTiling::DrmFormatModifier`, then `allocations` must contain exactly
501    ///   `self.drm_format_modifier().unwrap().1` elements.
502    pub fn bind_memory(
503        self,
504        allocations: impl IntoIterator<Item = ResourceMemory>,
505    ) -> Result<
506        Image,
507        (
508            Validated<VulkanError>,
509            RawImage,
510            impl ExactSizeIterator<Item = ResourceMemory>,
511        ),
512    > {
513        let allocations: SmallVec<[_; 4]> = allocations.into_iter().collect();
514
515        if let Err(err) = self.validate_bind_memory(&allocations) {
516            return Err((err.into(), self, allocations.into_iter()));
517        }
518
519        unsafe { self.bind_memory_unchecked(allocations) }.map_err(|(err, image, allocations)| {
520            (
521                err.into(),
522                image,
523                allocations
524                    .into_iter()
525                    .collect::<SmallVec<[_; 4]>>()
526                    .into_iter(),
527            )
528        })
529    }
530
531    fn validate_bind_memory(
532        &self,
533        allocations: &[ResourceMemory],
534    ) -> Result<(), Box<ValidationError>> {
535        if self.flags().intersects(ImageCreateFlags::SPARSE_BINDING) {
536            return Err(Box::new(ValidationError {
537                context: "self.flags()".into(),
538                problem: "contains `ImageCreateFlags::SPARSE_BINDING`".into(),
539                vuids: &["VUID-VkBindImageMemoryInfo-image-01045"],
540                ..Default::default()
541            }));
542        }
543
544        let physical_device = self.device().physical_device();
545
546        if self.flags.intersects(ImageCreateFlags::DISJOINT) {
547            match self.tiling {
548                ImageTiling::Optimal | ImageTiling::Linear => {
549                    if allocations.len() != self.format.planes().len() {
550                        return Err(Box::new(ValidationError {
551                            problem: "`self.flags()` contains `ImageCreateFlags::DISJOINT`, and \
552                                `self.tiling()` is `ImageTiling::Optimal` or \
553                                `ImageTiling::Linear`, but the length of `allocations` does not \
554                                equal the number of planes in the format of the image"
555                                .into(),
556                            ..Default::default()
557                        }));
558                    }
559                }
560                ImageTiling::DrmFormatModifier => {
561                    if allocations.len() != self.drm_format_modifier.unwrap().1 as usize {
562                        return Err(Box::new(ValidationError {
563                            problem: "`self.flags()` contains `ImageCreateFlags::DISJOINT`, and \
564                                `self.tiling()` is `ImageTiling::DrmFormatModifier`, but the \
565                                length of `allocations` does not equal the number of memory planes \
566                                of the DRM format modifier of the image"
567                                .into(),
568                            ..Default::default()
569                        }));
570                    }
571                }
572            }
573        } else {
574            if allocations.len() != 1 {
575                return Err(Box::new(ValidationError {
576                    problem: "`self.flags()` does not contain `ImageCreateFlags::DISJOINT`, but \
577                        the length of `allocations` is not 1"
578                        .into(),
579                    ..Default::default()
580                }));
581            }
582        }
583
584        for (index, (allocation, memory_requirements)) in allocations
585            .iter()
586            .zip(self.memory_requirements.iter())
587            .enumerate()
588        {
589            match allocation.allocation_type() {
590                AllocationType::Unknown => {
591                    // This allocation type is suitable for all image tilings by definition.
592                }
593                AllocationType::Linear => {
594                    if self.tiling() != ImageTiling::Linear {
595                        return Err(Box::new(ValidationError {
596                            problem: format!(
597                                "`allocations[{}].allocation_type()` is `AllocationType::Linear`, \
598                                but `self.tiling()` is not `ImageTiling::Linear`",
599                                index
600                            )
601                            .into(),
602                            ..Default::default()
603                        }));
604                    }
605                }
606                AllocationType::NonLinear => {
607                    if self.tiling() != ImageTiling::Optimal {
608                        return Err(Box::new(ValidationError {
609                            problem: format!(
610                                "`allocations[{}].allocation_type()` is \
611                                `AllocationType::NonLinear`, but `self.tiling()` is not \
612                                `ImageTiling::Optimal`",
613                                index
614                            )
615                            .into(),
616                            ..Default::default()
617                        }));
618                    }
619                }
620            }
621
622            let memory = allocation.device_memory();
623            let memory_offset = allocation.offset();
624            let memory_type = &physical_device.memory_properties().memory_types
625                [memory.memory_type_index() as usize];
626
627            // VUID-VkBindImageMemoryInfo-commonparent
628            assert_eq!(self.device(), memory.device());
629
630            // VUID-VkBindImageMemoryInfo-image-07460
631            // Ensured by taking ownership of `RawImage`.
632
633            // VUID-VkBindImageMemoryInfo-memoryOffset-01046
634            // Assume that `allocation` was created correctly.
635
636            if memory_requirements.memory_type_bits & (1 << memory.memory_type_index()) == 0 {
637                return Err(Box::new(ValidationError {
638                    problem: format!(
639                        "`allocation[{}].device_memory().memory_type_index()` is not a bit set in \
640                        `self.memory_requirements().memory_type_bits`",
641                        index
642                    )
643                    .into(),
644                    vuids: &[
645                        "VUID-VkBindImageMemoryInfo-pNext-01615",
646                        "VUID-VkBindImageMemoryInfo-pNext-01619",
647                    ],
648                    ..Default::default()
649                }));
650            }
651
652            if !is_aligned(memory_offset, memory_requirements.layout.alignment()) {
653                return Err(Box::new(ValidationError {
654                    problem: format!(
655                        "`allocations[{}].offset()` is not aligned according to \
656                        `self.memory_requirements().layout.alignment()`",
657                        index
658                    )
659                    .into(),
660                    vuids: &[
661                        "VUID-VkBindImageMemoryInfo-pNext-01616",
662                        "VUID-VkBindImageMemoryInfo-pNext-01620",
663                    ],
664                    ..Default::default()
665                }));
666            }
667
668            if allocation.size() < memory_requirements.layout.size() {
669                return Err(Box::new(ValidationError {
670                    problem: format!(
671                        "`allocations[{}].size()` is less than \
672                        `self.memory_requirements().layout.size()`",
673                        index
674                    )
675                    .into(),
676                    vuids: &[
677                        "VUID-VkBindImageMemoryInfo-pNext-01617",
678                        "VUID-VkBindImageMemoryInfo-pNext-01621",
679                    ],
680                    ..Default::default()
681                }));
682            }
683
684            if let Some(dedicated_to) = memory.dedicated_to() {
685                match dedicated_to {
686                    DedicatedTo::Image(id) if id == self.id => {}
687                    _ => {
688                        return Err(Box::new(ValidationError {
689                            problem: format!(
690                                "`allocations[{}].device_memory()` is a dedicated allocation, but \
691                                it is not dedicated to this image",
692                                index
693                            )
694                            .into(),
695                            vuids: &["VUID-VkBindImageMemoryInfo-memory-02628"],
696                            ..Default::default()
697                        }));
698                    }
699                }
700                debug_assert!(memory_offset == 0); // This should be ensured by the allocator
701            } else {
702                if memory_requirements.requires_dedicated_allocation {
703                    return Err(Box::new(ValidationError {
704                        problem: format!(
705                            "`self.memory_requirements().requires_dedicated_allocation` is \
706                            `true`, but `allocations[{}].device_memory()` is not a \
707                            dedicated allocation",
708                            index
709                        )
710                        .into(),
711                        vuids: &["VUID-VkBindImageMemoryInfo-image-01445"],
712                        ..Default::default()
713                    }));
714                }
715            }
716
717            if memory_type
718                .property_flags
719                .intersects(MemoryPropertyFlags::PROTECTED)
720            {
721                return Err(Box::new(ValidationError {
722                    problem: format!(
723                        "the `property_flags` of the memory type of \
724                        `allocations[{}].device_memory()` contains \
725                        `MemoryPropertyFlags::PROTECTED`",
726                        index
727                    )
728                    .into(),
729                    vuids: &["VUID-VkBindImageMemoryInfo-None-01901"],
730                    ..Default::default()
731                }));
732            }
733
734            if !memory.export_handle_types().is_empty() {
735                if !self
736                    .external_memory_handle_types
737                    .intersects(memory.export_handle_types())
738                {
739                    return Err(Box::new(ValidationError {
740                        problem: format!(
741                            "`allocations[{}].device_memory().export_handle_types()` is not empty, \
742                            but it does not share at least one memory type with \
743                            `self.external_memory_handle_types()`",
744                            index
745                        )
746                        .into(),
747                        vuids: &["VUID-VkBindImageMemoryInfo-memory-02728"],
748                        ..Default::default()
749                    }));
750                }
751
752                for handle_type in memory.export_handle_types() {
753                    let image_format_properties = unsafe {
754                        physical_device.image_format_properties_unchecked(ImageFormatInfo {
755                            flags: self.flags,
756                            format: self.format,
757                            view_formats: self.view_formats.clone(),
758                            image_type: self.image_type,
759                            tiling: self.tiling,
760                            usage: self.usage,
761                            stencil_usage: self.stencil_usage,
762                            drm_format_modifier_info: self.drm_format_modifier().map(
763                                |(drm_format_modifier, _)| ImageDrmFormatModifierInfo {
764                                    drm_format_modifier,
765                                    sharing: self.sharing.clone(),
766                                    _ne: crate::NonExhaustive(()),
767                                },
768                            ),
769                            external_memory_handle_type: Some(handle_type),
770                            image_view_type: None,
771                            _ne: crate::NonExhaustive(()),
772                        })
773                    }
774                    .map_err(|_| {
775                        Box::new(ValidationError {
776                            problem: "`PhysicalDevice::image_format_properties` returned an error"
777                                .into(),
778                            ..Default::default()
779                        })
780                    })?
781                    .unwrap();
782
783                    if image_format_properties
784                        .external_memory_properties
785                        .dedicated_only
786                        && !memory.is_dedicated()
787                    {
788                        return Err(Box::new(ValidationError {
789                            problem: format!(
790                                "`allocations[{}].device_memory().export_handle_types()` has the \
791                                `{:?}` flag set, which requires a dedicated allocation as returned \
792                                by `PhysicalDevice::image_format_properties`, but \
793                                `allocations[{}].device_memory()` is not a dedicated allocation",
794                                index, handle_type, index,
795                            )
796                            .into(),
797                            vuids: &["VUID-VkMemoryAllocateInfo-pNext-00639"],
798                            ..Default::default()
799                        }));
800                    }
801
802                    if !image_format_properties
803                        .external_memory_properties
804                        .exportable
805                    {
806                        return Err(Box::new(ValidationError {
807                            problem: format!(
808                                "`allocations[{}].device_memory().export_handle_types()` has the \
809                                `{:?}` flag set, but the flag is not supported for exporting, as \
810                                returned by `PhysicalDevice::image_format_properties`",
811                                index, handle_type,
812                            )
813                            .into(),
814                            vuids: &["VUID-VkExportMemoryAllocateInfo-handleTypes-00656"],
815                            ..Default::default()
816                        }));
817                    }
818
819                    if !image_format_properties
820                        .external_memory_properties
821                        .compatible_handle_types
822                        .contains(memory.export_handle_types())
823                    {
824                        return Err(Box::new(ValidationError {
825                            problem: format!(
826                                "`allocation.device_memory().export_handle_types()` has the `{:?}` \
827                                flag set, but the flag is not compatible with the other flags set, \
828                                as returned by `PhysicalDevice::image_format_properties`",
829                                handle_type,
830                            )
831                            .into(),
832                            vuids: &["VUID-VkExportMemoryAllocateInfo-handleTypes-00656"],
833                            ..Default::default()
834                        }));
835                    }
836                }
837            }
838
839            if let Some(handle_type) = memory.imported_handle_type() {
840                if !ExternalMemoryHandleTypes::from(handle_type)
841                    .intersects(self.external_memory_handle_types)
842                {
843                    return Err(Box::new(ValidationError {
844                        problem: format!(
845                            "`allocations[{}].device_memory()` is imported, but \
846                            `self.external_memory_handle_types()` does not contain the imported \
847                            handle type",
848                            index
849                        )
850                        .into(),
851                        vuids: &["VUID-VkBindImageMemoryInfo-memory-02989"],
852                        ..Default::default()
853                    }));
854                }
855            }
856        }
857
858        Ok(())
859    }
860
861    /// # Safety
862    ///
863    /// - If `self.flags()` does not contain `ImageCreateFlags::DISJOINT`, then `allocations` must
864    ///   contain exactly one element.
865    /// - If `self.flags()` contains `ImageCreateFlags::DISJOINT`, and `self.tiling()` is
866    ///   `ImageTiling::Linear` or `ImageTiling::Optimal`, then `allocations` must contain exactly
867    ///   `self.format().unwrap().planes().len()` elements.
868    /// - If `self.flags()` contains `ImageCreateFlags::DISJOINT`, and `self.tiling()` is
869    ///   `ImageTiling::DrmFormatModifier`, then `allocations` must contain exactly
870    ///   `self.drm_format_modifier().unwrap().1` elements.
871    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
872    pub unsafe fn bind_memory_unchecked(
873        self,
874        allocations: impl IntoIterator<Item = ResourceMemory>,
875    ) -> Result<
876        Image,
877        (
878            VulkanError,
879            RawImage,
880            impl ExactSizeIterator<Item = ResourceMemory>,
881        ),
882    > {
883        let allocations: SmallVec<[_; 4]> = allocations.into_iter().collect();
884
885        const PLANE_ASPECTS_VK_NORMAL: &[ash::vk::ImageAspectFlags] = &[
886            ash::vk::ImageAspectFlags::PLANE_0,
887            ash::vk::ImageAspectFlags::PLANE_1,
888            ash::vk::ImageAspectFlags::PLANE_2,
889        ];
890        const PLANE_ASPECTS_VK_DRM_FORMAT_MODIFIER: &[ash::vk::ImageAspectFlags] = &[
891            ash::vk::ImageAspectFlags::MEMORY_PLANE_0_EXT,
892            ash::vk::ImageAspectFlags::MEMORY_PLANE_1_EXT,
893            ash::vk::ImageAspectFlags::MEMORY_PLANE_2_EXT,
894            ash::vk::ImageAspectFlags::MEMORY_PLANE_3_EXT,
895        ];
896        let needs_plane = (self.device.api_version() >= Version::V1_1
897            || self.device.enabled_extensions().khr_bind_memory2)
898            && self.flags.intersects(ImageCreateFlags::DISJOINT);
899
900        let plane_aspects_vk = if needs_plane {
901            Some(match self.tiling {
902                // VUID-VkBindImagePlaneMemoryInfo-planeAspect-02283
903                ImageTiling::Optimal | ImageTiling::Linear => {
904                    let plane_count = self.format.planes().len();
905                    &PLANE_ASPECTS_VK_NORMAL[..plane_count]
906                }
907                // VUID-VkBindImagePlaneMemoryInfo-planeAspect-02284
908                ImageTiling::DrmFormatModifier => {
909                    let plane_count = self.drm_format_modifier.unwrap().1 as usize;
910                    &PLANE_ASPECTS_VK_DRM_FORMAT_MODIFIER[..plane_count]
911                }
912            })
913        } else {
914            debug_assert_eq!(allocations.len(), 1);
915            None
916        };
917
918        let mut plane_infos_vk: SmallVec<[_; 4]> = (0..allocations.len())
919            .map(|plane_num| {
920                plane_aspects_vk.map(|plane_aspects_vk| {
921                    let plane_aspect_vk = plane_aspects_vk[plane_num];
922                    ash::vk::BindImagePlaneMemoryInfo::default().plane_aspect(plane_aspect_vk)
923                })
924            })
925            .collect();
926
927        let infos_vk: SmallVec<[_; 4]> = allocations
928            .iter()
929            .zip(&mut plane_infos_vk)
930            .map(|(allocation, plane_info_vk)| {
931                let mut info_vk = allocation.to_vk_bind_image_memory_info(self.handle);
932
933                if let Some(next) = plane_info_vk {
934                    info_vk = info_vk.push_next(next);
935                }
936
937                info_vk
938            })
939            .collect();
940
941        let fns = self.device.fns();
942
943        let result = if self.device.api_version() >= Version::V1_1
944            || self.device.enabled_extensions().khr_bind_memory2
945        {
946            if self.device.api_version() >= Version::V1_1 {
947                unsafe {
948                    (fns.v1_1.bind_image_memory2)(
949                        self.device.handle(),
950                        infos_vk.len() as u32,
951                        infos_vk.as_ptr(),
952                    )
953                }
954            } else {
955                unsafe {
956                    (fns.khr_bind_memory2.bind_image_memory2_khr)(
957                        self.device.handle(),
958                        infos_vk.len() as u32,
959                        infos_vk.as_ptr(),
960                    )
961                }
962            }
963        } else {
964            let info_vk = &infos_vk[0];
965
966            unsafe {
967                (fns.v1_0.bind_image_memory)(
968                    self.device.handle(),
969                    info_vk.image,
970                    info_vk.memory,
971                    info_vk.memory_offset,
972                )
973            }
974        }
975        .result();
976
977        if let Err(err) = result {
978            return Err((VulkanError::from(err), self, allocations.into_iter()));
979        }
980
981        let layout = self.default_layout();
982
983        Ok(unsafe { Image::from_raw(self, ImageMemory::Normal(allocations), layout) })
984    }
985
986    /// Converts a raw image into a full image without binding any memory.
987    ///
988    /// # Safety
989    ///
990    /// If `self.flags()` does not contain [`ImageCreateFlags::SPARSE_BINDING`]:
991    ///
992    /// - The image must already have a suitable memory allocation bound to it.
993    ///
994    /// If `self.flags()` does contain [`ImageCreateFlags::SPARSE_BINDING`]:
995    ///
996    /// - If `self.flags()` does not contain [`ImageCreateFlags::SPARSE_RESIDENCY`], then the image
997    ///   must be fully bound with memory before any memory is accessed by the device.
998    /// - If `self.flags()` contains [`ImageCreateFlags::SPARSE_RESIDENCY`], then if the sparse
999    ///   memory requirements include [`ImageAspects::METADATA`], then the metadata mip tail must
1000    ///   be fully bound with memory before any memory is accessed by the device.
1001    /// - If `self.flags()` contains [`ImageCreateFlags::SPARSE_RESIDENCY`], then you must ensure
1002    ///   that any reads from the image are prepared to handle unexpected or inconsistent values,
1003    ///   as determined by the [`residency_non_resident_strict`] device property.
1004    ///
1005    /// [`residency_non_resident_strict`]: crate::device::DeviceProperties::residency_non_resident_strict
1006    pub unsafe fn assume_bound(self) -> Image {
1007        let memory = if self.flags().intersects(ImageCreateFlags::SPARSE_BINDING) {
1008            ImageMemory::Sparse
1009        } else {
1010            ImageMemory::External
1011        };
1012        let layout = self.default_layout();
1013
1014        unsafe { Image::from_raw(self, memory, layout) }
1015    }
1016
1017    fn default_layout(&self) -> ImageLayout {
1018        let usage = self
1019            .usage
1020            .difference(ImageUsage::TRANSFER_SRC | ImageUsage::TRANSFER_DST);
1021
1022        if usage.intersects(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT)
1023            && usage
1024                .difference(ImageUsage::SAMPLED | ImageUsage::INPUT_ATTACHMENT)
1025                .is_empty()
1026        {
1027            ImageLayout::ShaderReadOnlyOptimal
1028        } else if usage.intersects(ImageUsage::COLOR_ATTACHMENT)
1029            && usage.difference(ImageUsage::COLOR_ATTACHMENT).is_empty()
1030        {
1031            ImageLayout::ColorAttachmentOptimal
1032        } else if usage.intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
1033            && usage
1034                .difference(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
1035                .is_empty()
1036        {
1037            ImageLayout::DepthStencilAttachmentOptimal
1038        } else {
1039            ImageLayout::General
1040        }
1041    }
1042
1043    /// Returns the memory requirements for this image.
1044    ///
1045    /// - If the image is a swapchain image, this returns a slice with a length of 0.
1046    /// - If `self.flags()` does not contain `ImageCreateFlags::DISJOINT`, this returns a slice
1047    ///   with a length of 1.
1048    /// - If `self.flags()` does contain `ImageCreateFlags::DISJOINT`, this returns a slice with a
1049    ///   length equal to `self.format().unwrap().planes().len()`.
1050    #[inline]
1051    pub fn memory_requirements(&self) -> &[MemoryRequirements] {
1052        &self.memory_requirements
1053    }
1054
1055    /// Returns the sparse memory requirements for this image.
1056    ///
1057    /// If `self.flags()` does not contain both `ImageCreateFlags::SPARSE_BINDING` and
1058    /// `ImageCreateFlags::SPARSE_RESIDENCY`, this returns an empty slice.
1059    #[inline]
1060    pub fn sparse_memory_requirements(&self) -> &[SparseImageMemoryRequirements] {
1061        &self.sparse_memory_requirements
1062    }
1063
1064    /// Returns the flags the image was created with.
1065    #[inline]
1066    pub fn flags(&self) -> ImageCreateFlags {
1067        self.flags
1068    }
1069
1070    /// Returns the image type of the image.
1071    #[inline]
1072    pub fn image_type(&self) -> ImageType {
1073        self.image_type
1074    }
1075
1076    /// Returns the image's format.
1077    #[inline]
1078    pub fn format(&self) -> Format {
1079        self.format
1080    }
1081
1082    /// Returns the features supported by the image's format.
1083    #[inline]
1084    pub fn format_features(&self) -> FormatFeatures {
1085        self.format_features
1086    }
1087
1088    /// Returns the formats that an image view created from this image can have.
1089    #[inline]
1090    pub fn view_formats(&self) -> &[Format] {
1091        &self.view_formats
1092    }
1093
1094    /// Returns the extent of the image.
1095    #[inline]
1096    pub fn extent(&self) -> [u32; 3] {
1097        self.extent
1098    }
1099
1100    /// Returns the number of array layers in the image.
1101    #[inline]
1102    pub fn array_layers(&self) -> u32 {
1103        self.array_layers
1104    }
1105
1106    /// Returns the number of mip levels in the image.
1107    #[inline]
1108    pub fn mip_levels(&self) -> u32 {
1109        self.mip_levels
1110    }
1111
1112    /// Returns the initial layout of the image.
1113    #[inline]
1114    pub fn initial_layout(&self) -> ImageLayout {
1115        self.initial_layout
1116    }
1117
1118    /// Returns the number of samples for the image.
1119    #[inline]
1120    pub fn samples(&self) -> SampleCount {
1121        self.samples
1122    }
1123
1124    /// Returns the tiling of the image.
1125    #[inline]
1126    pub fn tiling(&self) -> ImageTiling {
1127        self.tiling
1128    }
1129
1130    /// Returns the usage the image was created with.
1131    #[inline]
1132    pub fn usage(&self) -> ImageUsage {
1133        self.usage
1134    }
1135
1136    /// Returns the stencil usage the image was created with.
1137    #[inline]
1138    pub fn stencil_usage(&self) -> Option<ImageUsage> {
1139        self.stencil_usage
1140    }
1141
1142    /// Returns the sharing the image was created with.
1143    #[inline]
1144    pub fn sharing(&self) -> &Sharing<SmallVec<[u32; 4]>> {
1145        &self.sharing
1146    }
1147
1148    /// If `self.tiling()` is `ImageTiling::DrmFormatModifier`, returns the DRM format modifier
1149    /// of the image, and the number of memory planes.
1150    /// This was either provided in [`ImageCreateInfo::drm_format_modifiers`], or if
1151    /// multiple modifiers were provided, selected from the list by the Vulkan implementation.
1152    #[inline]
1153    pub fn drm_format_modifier(&self) -> Option<(u64, u32)> {
1154        self.drm_format_modifier
1155    }
1156
1157    /// Returns the external memory handle types that are supported with this image.
1158    #[inline]
1159    pub fn external_memory_handle_types(&self) -> ExternalMemoryHandleTypes {
1160        self.external_memory_handle_types
1161    }
1162
1163    /// Returns an `ImageSubresourceLayers` covering the first mip level of the image. All aspects
1164    /// of the image are selected, or `plane0` if the image is multi-planar.
1165    #[inline]
1166    pub fn subresource_layers(&self) -> ImageSubresourceLayers {
1167        ImageSubresourceLayers {
1168            aspects: {
1169                let aspects = self.format.aspects();
1170
1171                if aspects.intersects(ImageAspects::PLANE_0) {
1172                    ImageAspects::PLANE_0
1173                } else {
1174                    aspects
1175                }
1176            },
1177            mip_level: 0,
1178            array_layers: 0..self.array_layers,
1179        }
1180    }
1181
1182    /// Returns an `ImageSubresourceRange` covering the whole image. If the image is multi-planar,
1183    /// only the `color` aspect is selected.
1184    #[inline]
1185    pub fn subresource_range(&self) -> ImageSubresourceRange {
1186        ImageSubresourceRange {
1187            aspects: self.format.aspects()
1188                - (ImageAspects::PLANE_0 | ImageAspects::PLANE_1 | ImageAspects::PLANE_2),
1189            mip_levels: 0..self.mip_levels,
1190            array_layers: 0..self.array_layers,
1191        }
1192    }
1193
1194    /// Queries the memory layout of a single subresource of the image.
1195    ///
1196    /// Only images with linear tiling are supported, if they do not have a format with both a
1197    /// depth and a stencil format. Images with optimal tiling have an opaque image layout that is
1198    /// not suitable for direct memory accesses, and likewise for combined depth/stencil formats.
1199    /// Multi-planar formats are supported, but you must specify one of the planes as the `aspect`,
1200    /// not [`ImageAspect::Color`].
1201    ///
1202    /// The results of this function are cached, so that future calls with the same arguments
1203    /// do not need to make a call to the Vulkan API again.
1204    pub fn subresource_layout(
1205        &self,
1206        aspect: ImageAspect,
1207        mip_level: u32,
1208        array_layer: u32,
1209    ) -> Result<SubresourceLayout, Box<ValidationError>> {
1210        self.validate_subresource_layout(aspect, mip_level, array_layer)?;
1211
1212        Ok(unsafe { self.subresource_layout_unchecked(aspect, mip_level, array_layer) })
1213    }
1214
1215    fn validate_subresource_layout(
1216        &self,
1217        aspect: ImageAspect,
1218        mip_level: u32,
1219        array_layer: u32,
1220    ) -> Result<(), Box<ValidationError>> {
1221        aspect.validate_device(&self.device).map_err(|err| {
1222            err.add_context("aspect")
1223                .set_vuids(&["VUID-VkImageSubresource-aspectMask-parameter"])
1224        })?;
1225
1226        // VUID-VkImageSubresource-aspectMask-requiredbitmask
1227        // VUID-vkGetImageSubresourceLayout-aspectMask-00997
1228        // Ensured by use of enum `ImageAspect`.
1229
1230        if !matches!(
1231            self.tiling,
1232            ImageTiling::Linear | ImageTiling::DrmFormatModifier
1233        ) {
1234            return Err(Box::new(ValidationError {
1235                context: "self.tiling()".into(),
1236                problem: "is not `ImageTiling::Linear` or `ImageTiling::DrmFormatModifier`".into(),
1237                vuids: &["VUID-vkGetImageSubresourceLayout-image-02270"],
1238                ..Default::default()
1239            }));
1240        }
1241
1242        if mip_level >= self.mip_levels {
1243            return Err(Box::new(ValidationError {
1244                context: "mip_level".into(),
1245                problem: "is greater than the number of mip levels in the image".into(),
1246                vuids: &["VUID-vkGetImageSubresourceLayout-mipLevel-01716"],
1247                ..Default::default()
1248            }));
1249        }
1250
1251        if array_layer >= self.array_layers {
1252            return Err(Box::new(ValidationError {
1253                context: "array_layer".into(),
1254                problem: "is greater than the number of array layers in the image".into(),
1255                vuids: &["VUID-vkGetImageSubresourceLayout-arrayLayer-01717"],
1256                ..Default::default()
1257            }));
1258        }
1259
1260        let format = self.format;
1261        let format_aspects = format.aspects();
1262
1263        if let Some((_, drm_format_modifier_plane_count)) = self.drm_format_modifier {
1264            match drm_format_modifier_plane_count {
1265                1 => {
1266                    if !matches!(aspect, ImageAspect::MemoryPlane0) {
1267                        return Err(Box::new(ValidationError {
1268                            problem: "the image has a DRM format modifier with 1 memory plane, \
1269                                but `aspect` is not `ImageAspect::MemoryPlane0`"
1270                                .into(),
1271                            vuids: &["VUID-vkGetImageSubresourceLayout-tiling-02271"],
1272                            ..Default::default()
1273                        }));
1274                    }
1275                }
1276                2 => {
1277                    if !matches!(
1278                        aspect,
1279                        ImageAspect::MemoryPlane0 | ImageAspect::MemoryPlane1
1280                    ) {
1281                        return Err(Box::new(ValidationError {
1282                            problem: "the image has a DRM format modifier with 2 memory planes, \
1283                                but `aspect` is not `ImageAspect::MemoryPlane0` or \
1284                                `ImageAspect::MemoryPlane1`"
1285                                .into(),
1286                            vuids: &["VUID-vkGetImageSubresourceLayout-tiling-02271"],
1287                            ..Default::default()
1288                        }));
1289                    }
1290                }
1291                3 => {
1292                    if !matches!(
1293                        aspect,
1294                        ImageAspect::MemoryPlane0
1295                            | ImageAspect::MemoryPlane1
1296                            | ImageAspect::MemoryPlane2
1297                    ) {
1298                        return Err(Box::new(ValidationError {
1299                            problem: "the image has a DRM format modifier with 3 memory planes, \
1300                                but `aspect` is not `ImageAspect::MemoryPlane0`, \
1301                                `ImageAspect::MemoryPlane1` or `ImageAspect::MemoryPlane2`"
1302                                .into(),
1303                            vuids: &["VUID-vkGetImageSubresourceLayout-tiling-02271"],
1304                            ..Default::default()
1305                        }));
1306                    }
1307                }
1308                4 => {
1309                    if !matches!(
1310                        aspect,
1311                        ImageAspect::MemoryPlane0
1312                            | ImageAspect::MemoryPlane1
1313                            | ImageAspect::MemoryPlane2
1314                            | ImageAspect::MemoryPlane3
1315                    ) {
1316                        return Err(Box::new(ValidationError {
1317                            problem: "the image has a DRM format modifier with 4 memory planes, \
1318                                but `aspect` is not `ImageAspect::MemoryPlane0`, \
1319                                `ImageAspect::MemoryPlane1`, `ImageAspect::MemoryPlane2` or \
1320                                `ImageAspect::MemoryPlane3`"
1321                                .into(),
1322                            vuids: &["VUID-vkGetImageSubresourceLayout-tiling-02271"],
1323                            ..Default::default()
1324                        }));
1325                    }
1326                }
1327                _ => unreachable!("image has more than 4 memory planes??"),
1328            }
1329        } else if format_aspects.contains(ImageAspects::DEPTH | ImageAspects::STENCIL) {
1330            // Follows from the combination of these three VUIDs. See:
1331            // https://github.com/KhronosGroup/Vulkan-Docs/issues/1942
1332            return Err(Box::new(ValidationError {
1333                context: "self.format()".into(),
1334                problem: "has both a depth and a stencil aspect".into(),
1335                vuids: &[
1336                    "VUID-vkGetImageSubresourceLayout-aspectMask-00997",
1337                    "VUID-vkGetImageSubresourceLayout-format-04462",
1338                    "VUID-vkGetImageSubresourceLayout-format-04463",
1339                ],
1340                ..Default::default()
1341            }));
1342        } else if format_aspects.intersects(ImageAspects::DEPTH) {
1343            if aspect != ImageAspect::Depth {
1344                return Err(Box::new(ValidationError {
1345                    problem: "`self.format()` is a depth format, but \
1346                        `aspect` is not `ImageAspect::Depth`"
1347                        .into(),
1348                    vuids: &["VUID-vkGetImageSubresourceLayout-format-04462"],
1349                    ..Default::default()
1350                }));
1351            }
1352        } else if format_aspects.intersects(ImageAspects::STENCIL) {
1353            if aspect != ImageAspect::Stencil {
1354                return Err(Box::new(ValidationError {
1355                    problem: "`self.format()` is a stencil format, but \
1356                        `aspect` is not `ImageAspect::Stencil`"
1357                        .into(),
1358                    vuids: &["VUID-vkGetImageSubresourceLayout-format-04463"],
1359                    ..Default::default()
1360                }));
1361            }
1362        } else if format_aspects.intersects(ImageAspects::COLOR) {
1363            if format.planes().is_empty() {
1364                if aspect != ImageAspect::Color {
1365                    return Err(Box::new(ValidationError {
1366                        problem: "`self.format()` is a color format with a single plane, but \
1367                        `aspect` is not `ImageAspect::Color`"
1368                            .into(),
1369                        vuids: &["VUID-vkGetImageSubresourceLayout-format-08886"],
1370                        ..Default::default()
1371                    }));
1372                }
1373            } else if format.planes().len() == 2 {
1374                if !matches!(aspect, ImageAspect::Plane0 | ImageAspect::Plane1) {
1375                    return Err(Box::new(ValidationError {
1376                        problem: "`self.format()` is a color format with two planes, but \
1377                            `aspect` is not `ImageAspect::Plane0` or `ImageAspect::Plane1`"
1378                            .into(),
1379                        vuids: &["VUID-vkGetImageSubresourceLayout-tiling-08717"],
1380                        ..Default::default()
1381                    }));
1382                }
1383            } else if format.planes().len() == 3 {
1384                if !matches!(
1385                    aspect,
1386                    ImageAspect::Plane0 | ImageAspect::Plane1 | ImageAspect::Plane2
1387                ) {
1388                    return Err(Box::new(ValidationError {
1389                        problem: "`self.format()` is a color format with three planes, but \
1390                            `aspect` is not `ImageAspect::Plane0`, `ImageAspect::Plane1` or \
1391                            `ImageAspect::Plane2`"
1392                            .into(),
1393                        vuids: &["VUID-vkGetImageSubresourceLayout-tiling-08717"],
1394                        ..Default::default()
1395                    }));
1396                }
1397            }
1398        }
1399
1400        // TODO:  VUID-vkGetImageSubresourceLayout-tiling-02271
1401        //if self.tiling == ImageTiling::DrmFormatModifier {
1402        // Only one-plane image importing is possible for now.
1403        //}
1404
1405        // VUID-vkGetImageSubresourceLayout-format-08886
1406        // VUID-vkGetImageSubresourceLayout-format-04462
1407        // VUID-vkGetImageSubresourceLayout-format-04463
1408        // VUID-vkGetImageSubresourceLayout-format-04464
1409        // VUID-vkGetImageSubresourceLayout-format-01581
1410        // VUID-vkGetImageSubresourceLayout-format-01582
1411        if !format_aspects.contains(aspect.into()) {
1412            return Err(Box::new(ValidationError {
1413                context: "array_layer".into(),
1414                problem: "is greater than the number of array layers in the image".into(),
1415                vuids: &["VUID-vkGetImageSubresourceLayout-arrayLayer-01717"],
1416                ..Default::default()
1417            }));
1418        }
1419
1420        Ok(())
1421    }
1422
1423    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1424    pub unsafe fn subresource_layout_unchecked(
1425        &self,
1426        aspect: ImageAspect,
1427        mip_level: u32,
1428        array_layer: u32,
1429    ) -> SubresourceLayout {
1430        self.subresource_layout.get_or_insert(
1431            (aspect, mip_level, array_layer),
1432            |&(aspect, mip_level, array_layer)| {
1433                let subresource_vk = ash::vk::ImageSubresource {
1434                    aspect_mask: aspect.into(),
1435                    mip_level,
1436                    array_layer,
1437                };
1438
1439                let ash::vk::SubresourceLayout {
1440                    offset,
1441                    size,
1442                    row_pitch,
1443                    array_pitch,
1444                    depth_pitch,
1445                } = {
1446                    let fns = self.device.fns();
1447                    let mut output = MaybeUninit::uninit();
1448                    unsafe {
1449                        (fns.v1_0.get_image_subresource_layout)(
1450                            self.device.handle(),
1451                            self.handle,
1452                            &subresource_vk,
1453                            output.as_mut_ptr(),
1454                        )
1455                    };
1456                    unsafe { output.assume_init() }
1457                };
1458
1459                SubresourceLayout {
1460                    offset,
1461                    size,
1462                    row_pitch,
1463                    array_pitch: (self.array_layers > 1).then_some(array_pitch),
1464                    depth_pitch: matches!(self.image_type, ImageType::Dim3d).then_some(depth_pitch),
1465                }
1466            },
1467        )
1468    }
1469}
1470
1471impl Drop for RawImage {
1472    #[inline]
1473    fn drop(&mut self) {
1474        if !self.needs_destruction {
1475            return;
1476        }
1477
1478        let fns = self.device.fns();
1479        unsafe { (fns.v1_0.destroy_image)(self.device.handle(), self.handle, ptr::null()) };
1480    }
1481}
1482
1483unsafe impl VulkanObject for RawImage {
1484    type Handle = ash::vk::Image;
1485
1486    #[inline]
1487    fn handle(&self) -> Self::Handle {
1488        self.handle
1489    }
1490}
1491
1492unsafe impl DeviceOwned for RawImage {
1493    #[inline]
1494    fn device(&self) -> &Arc<Device> {
1495        &self.device
1496    }
1497}
1498
1499impl_id_counter!(RawImage);
1500
1501/// Parameters to create a new `Image`.
1502#[derive(Clone, Debug)]
1503pub struct ImageCreateInfo {
1504    /// Additional properties of the image.
1505    ///
1506    /// The default value is empty.
1507    pub flags: ImageCreateFlags,
1508
1509    /// The basic image dimensionality to create the image with.
1510    ///
1511    /// The default value is `ImageType::Dim2d`.
1512    pub image_type: ImageType,
1513
1514    /// The format used to store the image data.
1515    ///
1516    /// The default value is `Format::UNDEFINED`.
1517    pub format: Format,
1518
1519    /// The formats that an image view can have when it is created from this image.
1520    ///
1521    /// If the list is not empty, and `flags` does not contain
1522    /// [`ImageCreateFlags::MUTABLE_FORMAT`], then the list must contain at most one element,
1523    /// otherwise any number of elements are allowed. The view formats must be compatible with
1524    /// `format`. If `flags` also contains [`ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`],
1525    /// then the view formats can also be uncompressed formats that are merely size-compatible
1526    /// with `format`.
1527    ///
1528    /// If the list is empty, then depending on `flags`, a view must have the same format as
1529    /// `format`, can have any compatible format, or additionally any uncompressed size-compatible
1530    /// format. However, this is less efficient than specifying the possible view formats
1531    /// in advance.
1532    ///
1533    /// If this is not empty, then the device API version must be at least 1.2, or the
1534    /// [`khr_image_format_list`] extension must be enabled on the device.
1535    ///
1536    /// The default value is empty.
1537    ///
1538    /// [`khr_image_format_list`]: crate::device::DeviceExtensions::khr_image_format_list
1539    pub view_formats: Vec<Format>,
1540
1541    /// The width, height and depth of the image.
1542    ///
1543    /// If `image_type` is `ImageType::Dim2d`, then the depth must be 1.
1544    /// If `image_type` is `ImageType::Dim1d`, then the height and depth must be 1.
1545    ///
1546    /// The default value is `[0; 3]`, which must be overridden.
1547    pub extent: [u32; 3],
1548
1549    /// The number of array layers to create the image with.
1550    ///
1551    /// On [portability
1552    /// subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
1553    /// devices, if `samples` is not [`SampleCount::Sample1`] and `array_layers` is not 1,
1554    /// the [`multisample_array_image`](crate::device::DeviceFeatures::multisample_array_image)
1555    /// feature must be enabled on the device.
1556    ///
1557    /// The default value is `1`.
1558    pub array_layers: u32,
1559
1560    /// The number of mip levels to create the image with.
1561    ///
1562    /// The default value is `1`.
1563    pub mip_levels: u32,
1564
1565    /// The number of samples per texel that the image should use.
1566    ///
1567    /// On [portability
1568    /// subset](crate::instance#portability-subset-devices-and-the-enumerate_portability-flag)
1569    /// devices, if `samples` is not [`SampleCount::Sample1`] and `array_layers` is not 1,
1570    /// the [`multisample_array_image`](crate::device::DeviceFeatures::multisample_array_image)
1571    /// feature must be enabled on the device.
1572    ///
1573    /// The default value is [`SampleCount::Sample1`].
1574    pub samples: SampleCount,
1575
1576    /// The memory arrangement of the texel blocks.
1577    ///
1578    /// The default value is [`ImageTiling::Optimal`].
1579    pub tiling: ImageTiling,
1580
1581    /// How the image is going to be used.
1582    ///
1583    /// The default value is empty, which must be overridden.
1584    pub usage: ImageUsage,
1585
1586    /// How the stencil aspect of the image is going to be used, if different from the regular
1587    /// `usage`.
1588    ///
1589    /// If this is `Some`, then the device API version must be at least 1.2, or the
1590    /// [`ext_separate_stencil_usage`](crate::device::DeviceExtensions::ext_separate_stencil_usage)
1591    /// extension must be enabled on the device. `format` must a stencil aspect.
1592    ///
1593    /// The default value is `None`.
1594    pub stencil_usage: Option<ImageUsage>,
1595
1596    /// Whether the image can be shared across multiple queues, or is limited to a single queue.
1597    ///
1598    /// The default value is [`Sharing::Exclusive`].
1599    pub sharing: Sharing<SmallVec<[u32; 4]>>,
1600
1601    /// The image layout that the image will have when it is created.
1602    ///
1603    /// The default value is [`ImageLayout::Undefined`].
1604    pub initial_layout: ImageLayout,
1605
1606    /// A list of possible Linux DRM format modifiers that the image may be created with. If
1607    /// `tiling` is [`ImageTiling::DrmFormatModifier`], then at least one DRM format modifier must
1608    /// be provided. Otherwise, this must be empty.
1609    ///
1610    /// If more than one DRM format modifier is provided, then the Vulkan driver will choose the
1611    /// modifier in an implementation-defined manner. You can query the modifier that has been
1612    /// chosen, after creating the image, by calling [`Image::drm_format_modifier`].
1613    ///
1614    /// If exactly one DRM format modifier is provided, the image will always be created with that
1615    /// modifier. You can then optionally specify the subresource layout of each memory plane with
1616    /// the `drm_format_modifier_plane_layouts` field.
1617    ///
1618    /// The default value is empty.
1619    pub drm_format_modifiers: Vec<u64>,
1620
1621    /// If `drm_format_modifiers` contains exactly one element, optionally specifies an explicit
1622    /// subresource layout for each memory plane of the image.
1623    ///
1624    /// If not empty, the number of provided subresource layouts must equal the number of memory
1625    /// planes for `drm_format_modifiers[0]`, as reported by
1626    /// [`DrmFormatModifierProperties::drm_format_modifier_plane_count`]. The following additional
1627    /// requirements apply to each element:
1628    /// - [`SubresourceLayout::size`] must always be 0.
1629    /// - If `array_layers` is 1, then [`SubresourceLayout::array_pitch`] must be `None`.
1630    /// - If `image_type` is not [`ImageType::Dim3d`] or `extent[2]` is 1, then
1631    ///   [`SubresourceLayout::depth_pitch`] must be `None`.
1632    ///
1633    /// If `drm_format_modifiers` does not contain exactly one element, then this must be empty.
1634    ///
1635    /// The default value is empty.
1636    pub drm_format_modifier_plane_layouts: Vec<SubresourceLayout>,
1637
1638    /// The external memory handle types that are going to be used with the image.
1639    ///
1640    /// If this is not empty, then the device API version must be at least 1.1, or the
1641    /// [`khr_external_memory`](crate::device::DeviceExtensions::khr_external_memory)
1642    /// extension must be enabled on the device. `initial_layout` must be set to
1643    /// [`ImageLayout::Undefined`].
1644    ///
1645    /// The default value is empty.
1646    pub external_memory_handle_types: ExternalMemoryHandleTypes,
1647
1648    pub _ne: crate::NonExhaustive,
1649}
1650
1651impl Default for ImageCreateInfo {
1652    #[inline]
1653    fn default() -> Self {
1654        Self {
1655            flags: ImageCreateFlags::empty(),
1656            image_type: ImageType::Dim2d,
1657            format: Format::UNDEFINED,
1658            view_formats: Vec::new(),
1659            extent: [0; 3],
1660            array_layers: 1,
1661            mip_levels: 1,
1662            samples: SampleCount::Sample1,
1663            tiling: ImageTiling::Optimal,
1664            usage: ImageUsage::empty(),
1665            stencil_usage: None,
1666            sharing: Sharing::Exclusive,
1667            initial_layout: ImageLayout::Undefined,
1668            external_memory_handle_types: ExternalMemoryHandleTypes::empty(),
1669            drm_format_modifiers: Vec::new(),
1670            drm_format_modifier_plane_layouts: Vec::new(),
1671            _ne: crate::NonExhaustive(()),
1672        }
1673    }
1674}
1675
1676impl ImageCreateInfo {
1677    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
1678        let &Self {
1679            flags,
1680            image_type,
1681            format,
1682            ref view_formats,
1683            extent,
1684            array_layers,
1685            mip_levels,
1686            samples,
1687            tiling,
1688            usage,
1689            stencil_usage,
1690            ref sharing,
1691            initial_layout,
1692            ref drm_format_modifiers,
1693            ref drm_format_modifier_plane_layouts,
1694            external_memory_handle_types,
1695            _ne: _,
1696        } = self;
1697
1698        let physical_device = device.physical_device();
1699        let device_properties = physical_device.properties();
1700
1701        flags.validate_device(device).map_err(|err| {
1702            err.add_context("flags")
1703                .set_vuids(&["VUID-VkImageCreateInfo-flags-parameter"])
1704        })?;
1705
1706        format.validate_device(device).map_err(|err| {
1707            err.add_context("format")
1708                .set_vuids(&["VUID-VkImageCreateInfo-format-parameter"])
1709        })?;
1710
1711        samples.validate_device(device).map_err(|err| {
1712            err.add_context("samples")
1713                .set_vuids(&["VUID-VkImageCreateInfo-samples-parameter"])
1714        })?;
1715
1716        tiling.validate_device(device).map_err(|err| {
1717            err.add_context("tiling")
1718                .set_vuids(&["VUID-VkImageCreateInfo-tiling-parameter"])
1719        })?;
1720
1721        usage.validate_device(device).map_err(|err| {
1722            err.add_context("usage")
1723                .set_vuids(&["VUID-VkImageCreateInfo-usage-parameter"])
1724        })?;
1725
1726        if usage.is_empty() {
1727            return Err(Box::new(ValidationError {
1728                context: "usage".into(),
1729                problem: "is empty".into(),
1730                vuids: &["VUID-VkImageCreateInfo-usage-requiredbitmask"],
1731                ..Default::default()
1732            }));
1733        }
1734
1735        if format == Format::UNDEFINED {
1736            return Err(Box::new(ValidationError {
1737                context: "format".into(),
1738                problem: "is `Format::UNDEFINED`".into(),
1739                vuids: &["VUID-VkImageCreateInfo-pNext-01975"],
1740                ..Default::default()
1741            }));
1742        }
1743
1744        let format_properties = unsafe { physical_device.format_properties_unchecked(format) };
1745        let image_create_drm_format_modifiers = &drm_format_modifiers;
1746        let image_create_maybe_linear = match tiling {
1747            ImageTiling::Linear => true,
1748            ImageTiling::Optimal => false,
1749            ImageTiling::DrmFormatModifier => {
1750                const DRM_FORMAT_MOD_LINEAR: u64 = 0;
1751                image_create_drm_format_modifiers.contains(&DRM_FORMAT_MOD_LINEAR)
1752            }
1753        };
1754        let image_create_format_features =
1755            format_properties.format_features(tiling, drm_format_modifiers);
1756
1757        initial_layout.validate_device(device).map_err(|err| {
1758            err.add_context("initial_layout")
1759                .set_vuids(&["VUID-VkImageCreateInfo-initialLayout-parameter"])
1760        })?;
1761
1762        if !matches!(
1763            initial_layout,
1764            ImageLayout::Undefined | ImageLayout::Preinitialized
1765        ) {
1766            return Err(Box::new(ValidationError {
1767                context: "initial_layout".into(),
1768                problem: "is not `ImageLayout::Undefined` or `ImageLayout::Preinitialized`".into(),
1769                vuids: &["VUID-VkImageCreateInfo-initialLayout-00993"],
1770                ..Default::default()
1771            }));
1772        }
1773
1774        if flags.intersects(ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE)
1775            && !flags.intersects(ImageCreateFlags::MUTABLE_FORMAT)
1776        {
1777            return Err(Box::new(ValidationError {
1778                context: "flags".into(),
1779                problem: "contains `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, but does not \
1780                    contain `ImageCreateFlags::MUTABLE_FORMAT`"
1781                    .into(),
1782                vuids: &["VUID-VkImageCreateInfo-flags-01573"],
1783                ..Default::default()
1784            }));
1785        }
1786
1787        if !view_formats.is_empty() {
1788            if !(device.api_version() >= Version::V1_2
1789                || device.enabled_extensions().khr_image_format_list)
1790            {
1791                return Err(Box::new(ValidationError {
1792                    context: "view_formats".into(),
1793                    problem: "is not empty".into(),
1794                    requires_one_of: RequiresOneOf(&[
1795                        RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]),
1796                        RequiresAllOf(&[Requires::DeviceExtension("khr_image_format_list")]),
1797                    ]),
1798                    ..Default::default()
1799                }));
1800            }
1801
1802            if !flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) && view_formats.len() != 1 {
1803                return Err(Box::new(ValidationError {
1804                    problem: "`flags` does not contain `ImageCreateFlags::MUTABLE_FORMAT`, but \
1805                        `view_formats` contains more than one element"
1806                        .into(),
1807                    vuids: &["VUID-VkImageCreateInfo-flags-04738"],
1808                    ..Default::default()
1809                }));
1810            }
1811
1812            for (index, &view_format) in view_formats.iter().enumerate() {
1813                view_format.validate_device(device).map_err(|err| {
1814                    err.add_context(format!("view_formats[{}]", index))
1815                        .set_vuids(&["VUID-VkImageFormatListCreateInfo-pViewFormats-parameter"])
1816                })?;
1817
1818                if flags.intersects(ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE)
1819                    && view_format.compression().is_none()
1820                {
1821                    if !(view_format.compatibility() == format.compatibility()
1822                        || view_format.block_size() == format.block_size())
1823                    {
1824                        return Err(Box::new(ValidationError {
1825                            problem: format!(
1826                                "`flags` contains \
1827                                `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, and \
1828                                `view_formats[{}]` is an uncompressed format, but \
1829                                it is not compatible with `format`, and \
1830                                does not have an equal block size",
1831                                index
1832                            )
1833                            .into(),
1834                            vuids: &["VUID-VkImageCreateInfo-pNext-06722"],
1835                            ..Default::default()
1836                        }));
1837                    }
1838                } else {
1839                    if view_format.compatibility() != format.compatibility() {
1840                        return Err(Box::new(ValidationError {
1841                            problem: format!(
1842                                "`flags` does not contain \
1843                                `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, or \
1844                                `view_format[{}]` is a compressed format, but \
1845                                it is not compatible with `create_info.format`",
1846                                index
1847                            )
1848                            .into(),
1849                            vuids: &["VUID-VkImageCreateInfo-pNext-06722"],
1850                            ..Default::default()
1851                        }));
1852                    }
1853                }
1854            }
1855        }
1856
1857        match image_type {
1858            ImageType::Dim1d => {
1859                if extent[1] != 1 {
1860                    return Err(Box::new(ValidationError {
1861                        problem: "`image_type` is `ImageType::Dim1d`, but `extent[1]` is not 1"
1862                            .into(),
1863                        vuids: &["VUID-VkImageCreateInfo-imageType-00956"],
1864                        ..Default::default()
1865                    }));
1866                }
1867
1868                if extent[2] != 1 {
1869                    return Err(Box::new(ValidationError {
1870                        problem: "`image_type` is `ImageType::Dim1d`, but `extent[2]` is not 1"
1871                            .into(),
1872                        vuids: &["VUID-VkImageCreateInfo-imageType-00956"],
1873                        ..Default::default()
1874                    }));
1875                }
1876
1877                if flags.intersects(ImageCreateFlags::SPARSE_RESIDENCY) {
1878                    return Err(Box::new(ValidationError {
1879                        problem: "`image_type` is `ImageType::Dim1d`, but `flags` contains \
1880                            `ImageCreateFlags::SPARSE_RESIDENCY`"
1881                            .into(),
1882                        vuids: &["VUID-VkImageCreateInfo-imageType-00970"],
1883                        ..Default::default()
1884                    }));
1885                }
1886            }
1887            ImageType::Dim2d => {
1888                if extent[2] != 1 {
1889                    return Err(Box::new(ValidationError {
1890                        problem: "`image_type` is `ImageType::Dim2d`, but `extent[2]` is not 1"
1891                            .into(),
1892                        vuids: &["VUID-VkImageCreateInfo-imageType-00957"],
1893                        ..Default::default()
1894                    }));
1895                }
1896
1897                if flags.intersects(ImageCreateFlags::SPARSE_RESIDENCY)
1898                    && !device.enabled_features().sparse_residency_image2_d
1899                {
1900                    return Err(Box::new(ValidationError {
1901                        problem: "`image_type` is `ImageType::Dim2d`, and `flags` contains \
1902                            `ImageCreateFlags::SPARSE_RESIDENCY`"
1903                            .into(),
1904                        requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
1905                            Requires::DeviceFeature("sparse_residency_image2_d"),
1906                        ])]),
1907                        vuids: &["VUID-VkImageCreateInfo-imageType-00971"],
1908                        ..Default::default()
1909                    }));
1910                }
1911            }
1912            ImageType::Dim3d => {
1913                if array_layers != 1 {
1914                    return Err(Box::new(ValidationError {
1915                        problem: "`image_type` is `ImageType::Dim3d`, but `array_layers` is not 1"
1916                            .into(),
1917                        vuids: &["VUID-VkImageCreateInfo-imageType-00961"],
1918                        ..Default::default()
1919                    }));
1920                }
1921
1922                if flags.intersects(ImageCreateFlags::SPARSE_RESIDENCY)
1923                    && !device.enabled_features().sparse_residency_image3_d
1924                {
1925                    return Err(Box::new(ValidationError {
1926                        problem: "`image_type` is `ImageType::Dim3d`, and `flags` contains \
1927                            `ImageCreateFlags::SPARSE_RESIDENCY`"
1928                            .into(),
1929                        requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
1930                            Requires::DeviceFeature("sparse_residency_image3_d"),
1931                        ])]),
1932                        vuids: &["VUID-VkImageCreateInfo-imageType-00972"],
1933                        ..Default::default()
1934                    }));
1935                }
1936            }
1937        }
1938
1939        if extent[0] == 0 {
1940            return Err(Box::new(ValidationError {
1941                context: "extent[0]".into(),
1942                problem: "is zero".into(),
1943                vuids: &["VUID-VkImageCreateInfo-extent-00944"],
1944                ..Default::default()
1945            }));
1946        }
1947
1948        if extent[1] == 0 {
1949            return Err(Box::new(ValidationError {
1950                context: "extent[1]".into(),
1951                problem: "is zero".into(),
1952                vuids: &["VUID-VkImageCreateInfo-extent-00945"],
1953                ..Default::default()
1954            }));
1955        }
1956
1957        if extent[2] == 0 {
1958            return Err(Box::new(ValidationError {
1959                context: "extent[2]".into(),
1960                problem: "is zero".into(),
1961                vuids: &["VUID-VkImageCreateInfo-extent-00946"],
1962                ..Default::default()
1963            }));
1964        }
1965
1966        if array_layers == 0 {
1967            return Err(Box::new(ValidationError {
1968                context: "array_layers".into(),
1969                problem: "is zero".into(),
1970                vuids: &["VUID-VkImageCreateInfo-arrayLayers-00948"],
1971                ..Default::default()
1972            }));
1973        }
1974
1975        if mip_levels == 0 {
1976            return Err(Box::new(ValidationError {
1977                context: "mip_levels".into(),
1978                problem: "is zero".into(),
1979                vuids: &["VUID-VkImageCreateInfo-mipLevels-00947"],
1980                ..Default::default()
1981            }));
1982        }
1983
1984        let max_mip_levels = max_mip_levels(extent);
1985        debug_assert!(max_mip_levels >= 1);
1986
1987        if mip_levels > max_mip_levels {
1988            return Err(Box::new(ValidationError {
1989                problem: "`mip_levels` is greater than the maximum allowed number of mip levels \
1990                    for `dimensions`"
1991                    .into(),
1992                vuids: &["VUID-VkImageCreateInfo-mipLevels-00958"],
1993                ..Default::default()
1994            }));
1995        }
1996
1997        if samples != SampleCount::Sample1 {
1998            if image_type != ImageType::Dim2d {
1999                return Err(Box::new(ValidationError {
2000                    problem: "`samples` is not `samples != SampleCount::Sample1`, but \
2001                        `image_type` is not `ImageType::Dim2d`"
2002                        .into(),
2003                    vuids: &["VUID-VkImageCreateInfo-samples-02257"],
2004                    ..Default::default()
2005                }));
2006            }
2007
2008            if flags.intersects(ImageCreateFlags::CUBE_COMPATIBLE) {
2009                return Err(Box::new(ValidationError {
2010                    problem: "`samples` is not `samples != SampleCount::Sample1`, but \
2011                        `flags` contains `ImageCreateFlags::CUBE_COMPATIBLE`"
2012                        .into(),
2013                    vuids: &["VUID-VkImageCreateInfo-samples-02257"],
2014                    ..Default::default()
2015                }));
2016            }
2017
2018            if mip_levels != 1 {
2019                return Err(Box::new(ValidationError {
2020                    problem: "`samples` is not `samples != SampleCount::Sample1`, but \
2021                        `mip_levels` is not 1"
2022                        .into(),
2023                    vuids: &["VUID-VkImageCreateInfo-samples-02257"],
2024                    ..Default::default()
2025                }));
2026            }
2027
2028            if image_create_maybe_linear {
2029                return Err(Box::new(ValidationError {
2030                    problem: "`samples` is not `samples != SampleCount::Sample1`, but \
2031                        the image may have linear tiling"
2032                        .into(),
2033                    vuids: &["VUID-VkImageCreateInfo-samples-02257"],
2034                    ..Default::default()
2035                }));
2036            }
2037
2038            if device.enabled_extensions().khr_portability_subset
2039                && !device.enabled_features().multisample_array_image
2040                && array_layers != 1
2041            {
2042                return Err(Box::new(ValidationError {
2043                    problem: "this device is a portability subset device, \
2044                        `samples` is not `SampleCount::Sample1`, and \
2045                        `array_layers` is greater than 1"
2046                        .into(),
2047                    requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
2048                        "multisample_array_image",
2049                    )])]),
2050                    vuids: &["VUID-VkImageCreateInfo-multisampleArrayImage-04460"],
2051                    ..Default::default()
2052                }));
2053            }
2054        }
2055
2056        // Check limits for YCbCr formats
2057        if let Some(chroma_sampling) = format.ycbcr_chroma_sampling() {
2058            if mip_levels != 1 {
2059                return Err(Box::new(ValidationError {
2060                    problem: "`format` is a YCbCr format, but \
2061                        `mip_levels` is not 1"
2062                        .into(),
2063                    vuids: &["VUID-VkImageCreateInfo-format-06410"],
2064                    ..Default::default()
2065                }));
2066            }
2067
2068            if samples != SampleCount::Sample1 {
2069                return Err(Box::new(ValidationError {
2070                    problem: "`format` is a YCbCr format, but \
2071                        `samples` is not `SampleCount::Sample1`"
2072                        .into(),
2073                    vuids: &["VUID-VkImageCreateInfo-format-06411"],
2074                    ..Default::default()
2075                }));
2076            }
2077
2078            if image_type != ImageType::Dim2d {
2079                return Err(Box::new(ValidationError {
2080                    problem: "`format` is a YCbCr format, but \
2081                        `image_type` is not `ImageType::Dim2d`"
2082                        .into(),
2083                    vuids: &["VUID-VkImageCreateInfo-format-06412"],
2084                    ..Default::default()
2085                }));
2086            }
2087
2088            if array_layers > 1 && !device.enabled_features().ycbcr_image_arrays {
2089                return Err(Box::new(ValidationError {
2090                    problem: "`format` is is a YCbCr format, and \
2091                        `array_layers` is greater than 1"
2092                        .into(),
2093                    requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
2094                        "ycbcr_image_arrays",
2095                    )])]),
2096                    vuids: &["VUID-VkImageCreateInfo-format-06413"],
2097                    ..Default::default()
2098                }));
2099            }
2100
2101            match chroma_sampling {
2102                ChromaSampling::Mode444 => (),
2103                ChromaSampling::Mode422 => {
2104                    if extent[0] % 2 != 0 {
2105                        return Err(Box::new(ValidationError {
2106                            problem: "`format` is a YCbCr format with horizontal \
2107                                chroma subsampling, but \
2108                                `extent[0]` is not \
2109                                a multiple of 2"
2110                                .into(),
2111                            vuids: &["VUID-VkImageCreateInfo-format-04712"],
2112                            ..Default::default()
2113                        }));
2114                    }
2115                }
2116                ChromaSampling::Mode420 => {
2117                    if !(extent[0] % 2 == 0 && extent[1] % 2 == 0) {
2118                        return Err(Box::new(ValidationError {
2119                            problem: "`format` is a YCbCr format with horizontal and vertical \
2120                                chroma subsampling, but \
2121                                `extent[0]` and `extent[1]` are not both \
2122                                a multiple of 2"
2123                                .into(),
2124                            vuids: &[
2125                                "VUID-VkImageCreateInfo-format-04712",
2126                                "VUID-VkImageCreateInfo-format-04713",
2127                            ],
2128                            ..Default::default()
2129                        }));
2130                    }
2131                }
2132            }
2133        }
2134
2135        /* Check usage requirements */
2136
2137        if usage.intersects(ImageUsage::STORAGE)
2138            && samples != SampleCount::Sample1
2139            && !device.enabled_features().shader_storage_image_multisample
2140        {
2141            return Err(Box::new(ValidationError {
2142                problem: "`usage` contains `ImageUsage::STORAGE`, but \
2143                    `samples` is not `SampleCount::Sample1`"
2144                    .into(),
2145                requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
2146                    "shader_storage_image_multisample",
2147                )])]),
2148                vuids: &["VUID-VkImageCreateInfo-usage-00968"],
2149                ..Default::default()
2150            }));
2151        }
2152
2153        if usage.intersects(ImageUsage::TRANSIENT_ATTACHMENT) {
2154            if !usage.intersects(
2155                ImageUsage::COLOR_ATTACHMENT
2156                    | ImageUsage::DEPTH_STENCIL_ATTACHMENT
2157                    | ImageUsage::INPUT_ATTACHMENT,
2158            ) {
2159                return Err(Box::new(ValidationError {
2160                    context: "usage".into(),
2161                    problem: "contains `ImageUsage::TRANSIENT_ATTACHMENT`, but does not also \
2162                        contain one of `ImageUsage::COLOR_ATTACHMENT`, \
2163                        `ImageUsage::DEPTH_STENCIL_ATTACHMENT` or `ImageUsage::INPUT_ATTACHMENT`"
2164                        .into(),
2165                    vuids: &["VUID-VkImageCreateInfo-usage-00966"],
2166                    ..Default::default()
2167                }));
2168            }
2169
2170            if !(usage
2171                - (ImageUsage::TRANSIENT_ATTACHMENT
2172                    | ImageUsage::COLOR_ATTACHMENT
2173                    | ImageUsage::DEPTH_STENCIL_ATTACHMENT
2174                    | ImageUsage::INPUT_ATTACHMENT))
2175                .is_empty()
2176            {
2177                return Err(Box::new(ValidationError {
2178                    context: "usage".into(),
2179                    problem: "contains `ImageUsage::TRANSIENT_ATTACHMENT`, but also contains \
2180                        usages other than `ImageUsage::COLOR_ATTACHMENT`, \
2181                        `ImageUsage::DEPTH_STENCIL_ATTACHMENT` or `ImageUsage::INPUT_ATTACHMENT`"
2182                        .into(),
2183                    vuids: &["VUID-VkImageCreateInfo-usage-00963"],
2184                    ..Default::default()
2185                }));
2186            }
2187        }
2188
2189        if usage.intersects(
2190            ImageUsage::COLOR_ATTACHMENT
2191                | ImageUsage::DEPTH_STENCIL_ATTACHMENT
2192                | ImageUsage::INPUT_ATTACHMENT
2193                | ImageUsage::TRANSIENT_ATTACHMENT,
2194        ) {
2195            if extent[0] > device_properties.max_framebuffer_width {
2196                return Err(Box::new(ValidationError {
2197                    problem: "`usage` contains \
2198                        `ImageUsage::COLOR_ATTACHMENT`, \
2199                        `ImageUsage::DEPTH_STENCIL_ATTACHMENT`, \
2200                        `ImageUsage::INPUT_ATTACHMENT`, or \
2201                        `ImageUsage::TRANSIENT_ATTACHMENT`, but \
2202                        `extent[0]` exceeds the `max_framebuffer_width` limit"
2203                        .into(),
2204                    vuids: &[
2205                        "VUID-VkImageCreateInfo-usage-00964",
2206                        "VUID-VkImageCreateInfo-Format-02536",
2207                    ],
2208                    ..Default::default()
2209                }));
2210            }
2211
2212            if extent[1] > device_properties.max_framebuffer_height {
2213                return Err(Box::new(ValidationError {
2214                    problem: "`usage` contains \
2215                        `ImageUsage::COLOR_ATTACHMENT`, \
2216                        `ImageUsage::DEPTH_STENCIL_ATTACHMENT`, \
2217                        `ImageUsage::INPUT_ATTACHMENT`, or \
2218                        `ImageUsage::TRANSIENT_ATTACHMENT`, but \
2219                        `extent[1]` exceeds the `max_framebuffer_height` limit"
2220                        .into(),
2221                    vuids: &["VUID-VkImageCreateInfo-usage-00965"],
2222                    ..Default::default()
2223                }));
2224            }
2225        }
2226
2227        if let Some(stencil_usage) = stencil_usage {
2228            if !(device.api_version() >= Version::V1_2
2229                || device.enabled_extensions().ext_separate_stencil_usage)
2230            {
2231                return Err(Box::new(ValidationError {
2232                    context: "stencil_usage".into(),
2233                    problem: "is `Some`".into(),
2234                    requires_one_of: RequiresOneOf(&[
2235                        RequiresAllOf(&[Requires::APIVersion(Version::V1_2)]),
2236                        RequiresAllOf(&[Requires::DeviceExtension("ext_separate_stencil_usage")]),
2237                    ]),
2238                    ..Default::default()
2239                }));
2240            }
2241
2242            stencil_usage.validate_device(device).map_err(|err| {
2243                err.add_context("stencil_usage")
2244                    .set_vuids(&["VUID-VkImageStencilUsageCreateInfo-stencilUsage-parameter"])
2245            })?;
2246
2247            if stencil_usage.is_empty() {
2248                return Err(Box::new(ValidationError {
2249                    context: "stencil_usage".into(),
2250                    problem: "is empty".into(),
2251                    vuids: &["VUID-VkImageStencilUsageCreateInfo-usage-requiredbitmask"],
2252                    ..Default::default()
2253                }));
2254            }
2255
2256            if stencil_usage.intersects(ImageUsage::TRANSIENT_ATTACHMENT)
2257                && !(stencil_usage
2258                    - (ImageUsage::TRANSIENT_ATTACHMENT
2259                        | ImageUsage::DEPTH_STENCIL_ATTACHMENT
2260                        | ImageUsage::INPUT_ATTACHMENT))
2261                    .is_empty()
2262            {
2263                return Err(Box::new(ValidationError {
2264                    context: "stencil_usage".into(),
2265                    problem: "contains `ImageUsage::TRANSIENT_ATTACHMENT`, but also contains \
2266                        usages other than `ImageUsage::DEPTH_STENCIL_ATTACHMENT` or \
2267                        `ImageUsage::INPUT_ATTACHMENT`"
2268                        .into(),
2269                    vuids: &["VUID-VkImageStencilUsageCreateInfo-stencilUsage-02539"],
2270                    ..Default::default()
2271                }));
2272            }
2273
2274            if format
2275                .aspects()
2276                .intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
2277            {
2278                if usage.intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
2279                    && !stencil_usage.intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
2280                {
2281                    return Err(Box::new(ValidationError {
2282                        problem: "`format` is a depth/stencil format, and \
2283                            `usage` contains `ImageUsage::DEPTH_STENCIL_ATTACHMENT`, but \
2284                            `stencil_usage` does not also contain \
2285                            `ImageUsage::DEPTH_STENCIL_ATTACHMENT`"
2286                            .into(),
2287                        vuids: &["VUID-VkImageCreateInfo-format-02795"],
2288                        ..Default::default()
2289                    }));
2290                }
2291
2292                if !usage.intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
2293                    && stencil_usage.intersects(ImageUsage::DEPTH_STENCIL_ATTACHMENT)
2294                {
2295                    return Err(Box::new(ValidationError {
2296                        problem: "`format` is a depth/stencil format, and \
2297                            `usage` does not contain `ImageUsage::DEPTH_STENCIL_ATTACHMENT`, but \
2298                            `stencil_usage` does contain `ImageUsage::DEPTH_STENCIL_ATTACHMENT`"
2299                            .into(),
2300                        vuids: &["VUID-VkImageCreateInfo-format-02796"],
2301                        ..Default::default()
2302                    }));
2303                }
2304
2305                if usage.intersects(ImageUsage::TRANSIENT_ATTACHMENT)
2306                    && !stencil_usage.intersects(ImageUsage::TRANSIENT_ATTACHMENT)
2307                {
2308                    return Err(Box::new(ValidationError {
2309                        problem: "`format` is a depth/stencil format, and \
2310                            `usage` contains `ImageUsage::TRANSIENT_ATTACHMENT`, but \
2311                            `stencil_usage` does not also contain \
2312                            `ImageUsage::TRANSIENT_ATTACHMENT`"
2313                            .into(),
2314                        vuids: &["VUID-VkImageCreateInfo-format-02797"],
2315                        ..Default::default()
2316                    }));
2317                }
2318
2319                if !usage.intersects(ImageUsage::TRANSIENT_ATTACHMENT)
2320                    && stencil_usage.intersects(ImageUsage::TRANSIENT_ATTACHMENT)
2321                {
2322                    return Err(Box::new(ValidationError {
2323                        problem: "`format` is a depth/stencil format, and \
2324                            `usage` does not contain `ImageUsage::TRANSIENT_ATTACHMENT`, but \
2325                            `stencil_usage` does contain \
2326                            `ImageUsage::TRANSIENT_ATTACHMENT`"
2327                            .into(),
2328                        vuids: &["VUID-VkImageCreateInfo-format-02798"],
2329                        ..Default::default()
2330                    }));
2331                }
2332
2333                if stencil_usage.intersects(ImageUsage::INPUT_ATTACHMENT) {
2334                    if extent[0] > device_properties.max_framebuffer_width {
2335                        return Err(Box::new(ValidationError {
2336                            problem: "`stencil_usage` contains \
2337                                `ImageUsage::INPUT_ATTACHMENT`, but \
2338                                `extent[0]` exceeds the `max_framebuffer_width` limit"
2339                                .into(),
2340                            vuids: &["VUID-VkImageCreateInfo-Format-02536"],
2341                            ..Default::default()
2342                        }));
2343                    }
2344
2345                    if extent[1] > device_properties.max_framebuffer_height {
2346                        return Err(Box::new(ValidationError {
2347                            problem: "`stencil_usage` contains \
2348                                `ImageUsage::INPUT_ATTACHMENT`, but \
2349                                `extent[1]` exceeds the `max_framebuffer_height` limit"
2350                                .into(),
2351                            vuids: &["VUID-VkImageCreateInfo-format-02537"],
2352                            ..Default::default()
2353                        }));
2354                    }
2355                }
2356
2357                if stencil_usage.intersects(ImageUsage::STORAGE)
2358                    && samples != SampleCount::Sample1
2359                    && !device.enabled_features().shader_storage_image_multisample
2360                {
2361                    return Err(Box::new(ValidationError {
2362                        problem: "`stencil_usage` contains `ImageUsage::STORAGE`, but \
2363                            `samples` is not `SampleCount::Sample1`"
2364                            .into(),
2365                        requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
2366                            Requires::DeviceFeature("shader_storage_image_multisample"),
2367                        ])]),
2368                        vuids: &["VUID-VkImageCreateInfo-format-02538"],
2369                        ..Default::default()
2370                    }));
2371                }
2372            }
2373        }
2374
2375        /* Check flags requirements */
2376
2377        if flags.intersects(ImageCreateFlags::SPARSE_BINDING) {
2378            if !device.enabled_features().sparse_binding {
2379                return Err(Box::new(ValidationError {
2380                    context: "flags".into(),
2381                    problem: "contains `ImageCreateFlags::SPARSE_BINDING`".into(),
2382                    requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
2383                        "sparse_binding",
2384                    )])]),
2385                    vuids: &["VUID-VkImageCreateInfo-flags-00969"],
2386                }));
2387            }
2388
2389            if usage.intersects(ImageUsage::TRANSIENT_ATTACHMENT) {
2390                return Err(Box::new(ValidationError {
2391                    problem: "`flags` contains `ImageCreateFlags::SPARSE_BINDING`, but \
2392                        `usage` contains `ImageUsage::TRANSIENT_ATTACHMENT`"
2393                        .into(),
2394                    vuids: &["VUID-VkImageCreateInfo-None-01925"],
2395                    ..Default::default()
2396                }));
2397            }
2398        }
2399
2400        if flags.intersects(ImageCreateFlags::SPARSE_RESIDENCY) {
2401            if !flags.intersects(ImageCreateFlags::SPARSE_BINDING) {
2402                return Err(Box::new(ValidationError {
2403                    context: "flags".into(),
2404                    problem: "contains `ImageCreateFlags::SPARSE_RESIDENCY`, but does not also \
2405                        contain `ImageCreateFlags::SPARSE_BINDING`"
2406                        .into(),
2407                    vuids: &["VUID-VkImageCreateInfo-flags-00987"],
2408                    ..Default::default()
2409                }));
2410            }
2411
2412            if tiling == ImageTiling::Linear {
2413                return Err(Box::new(ValidationError {
2414                    context: "flags".into(),
2415                    problem: "contains `ImageCreateFlags::SPARSE_RESIDENCY`, but `tiling` is \
2416                        `ImageTiling::Linear`"
2417                        .into(),
2418                    vuids: &["VUID-VkImageCreateInfo-tiling-04121"],
2419                    ..Default::default()
2420                }));
2421            }
2422
2423            match samples {
2424                SampleCount::Sample2 => {
2425                    if !device.enabled_features().sparse_residency2_samples {
2426                        return Err(Box::new(ValidationError {
2427                            problem: "`flags` contains `ImageCreateFlags::SPARSE_RESIDENCY`, and \
2428                                `samples` is `SampleCount::Sample2`"
2429                                .into(),
2430                            requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
2431                                Requires::DeviceFeature("sparse_residency2_samples"),
2432                            ])]),
2433                            vuids: &["VUID-VkImageCreateInfo-imageType-00973"],
2434                            ..Default::default()
2435                        }));
2436                    }
2437                }
2438                SampleCount::Sample4 => {
2439                    if !device.enabled_features().sparse_residency4_samples {
2440                        return Err(Box::new(ValidationError {
2441                            problem: "`flags` contains `ImageCreateFlags::SPARSE_RESIDENCY`, and \
2442                                `samples` is `SampleCount::Sample4`"
2443                                .into(),
2444                            requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
2445                                Requires::DeviceFeature("sparse_residency4_samples"),
2446                            ])]),
2447                            vuids: &["VUID-VkImageCreateInfo-imageType-00974"],
2448                            ..Default::default()
2449                        }));
2450                    }
2451                }
2452                SampleCount::Sample8 => {
2453                    if !device.enabled_features().sparse_residency8_samples {
2454                        return Err(Box::new(ValidationError {
2455                            problem: "`flags` contains `ImageCreateFlags::SPARSE_RESIDENCY`, and \
2456                                `samples` is `SampleCount::Sample8`"
2457                                .into(),
2458                            requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
2459                                Requires::DeviceFeature("sparse_residency8_samples"),
2460                            ])]),
2461                            vuids: &["VUID-VkImageCreateInfo-imageType-00975"],
2462                            ..Default::default()
2463                        }));
2464                    }
2465                }
2466                SampleCount::Sample16 => {
2467                    if !device.enabled_features().sparse_residency16_samples {
2468                        return Err(Box::new(ValidationError {
2469                            problem: "`flags` contains `ImageCreateFlags::SPARSE_RESIDENCY`, and \
2470                                `samples` is `SampleCount::Sample16`"
2471                                .into(),
2472                            requires_one_of: RequiresOneOf(&[RequiresAllOf(&[
2473                                Requires::DeviceFeature("sparse_residency16_samples"),
2474                            ])]),
2475                            vuids: &["VUID-VkImageCreateInfo-imageType-00976"],
2476                            ..Default::default()
2477                        }));
2478                    }
2479                }
2480                SampleCount::Sample1 | SampleCount::Sample32 | SampleCount::Sample64 => (),
2481            }
2482        }
2483
2484        if flags.intersects(ImageCreateFlags::CUBE_COMPATIBLE) {
2485            if image_type != ImageType::Dim2d {
2486                return Err(Box::new(ValidationError {
2487                    problem: "`flags` contains `ImageCreateFlags::CUBE_COMPATIBLE`, but \
2488                        `image_type` is not `ImageType::Dim2d`"
2489                        .into(),
2490                    vuids: &["VUID-VkImageCreateInfo-flags-00949"],
2491                    ..Default::default()
2492                }));
2493            }
2494
2495            if extent[0] != extent[1] {
2496                return Err(Box::new(ValidationError {
2497                    problem: "`flags` contains `ImageCreateFlags::CUBE_COMPATIBLE`, but \
2498                        `extent[0]` does not equal `extent[1]`"
2499                        .into(),
2500                    vuids: &["VUID-VkImageCreateInfo-imageType-00954"],
2501                    ..Default::default()
2502                }));
2503            }
2504
2505            if array_layers < 6 {
2506                return Err(Box::new(ValidationError {
2507                    problem: "`flags` contains `ImageCreateFlags::CUBE_COMPATIBLE`, but \
2508                        `array_layers` is less than 6"
2509                        .into(),
2510                    vuids: &["VUID-VkImageCreateInfo-imageType-00954"],
2511                    ..Default::default()
2512                }));
2513            }
2514        }
2515
2516        if flags.intersects(ImageCreateFlags::DIM2D_ARRAY_COMPATIBLE) {
2517            if flags.intersects(ImageCreateFlags::SPARSE_BINDING) {
2518                return Err(Box::new(ValidationError {
2519                    context: "flags".into(),
2520                    problem: "contains `ImageCreateFlags::DIM2D_ARRAY_COMPATIBLE`, but \
2521                        also contains `ImageCreateFlags::SPARSE_BINDING`"
2522                        .into(),
2523                    vuids: &["VUID-VkImageCreateInfo-flags-09403"],
2524                    ..Default::default()
2525                }));
2526            }
2527
2528            if image_type != ImageType::Dim3d {
2529                return Err(Box::new(ValidationError {
2530                    problem: "`flags` contains `ImageCreateFlags::DIM2D_ARRAY_COMPATIBLE`, but \
2531                        `image_type` is not `ImageType::Dim3d`"
2532                        .into(),
2533                    vuids: &["VUID-VkImageCreateInfo-flags-00950"],
2534                    ..Default::default()
2535                }));
2536            }
2537
2538            if device.enabled_extensions().khr_portability_subset
2539                && !device.enabled_features().image_view2_d_on3_d_image
2540            {
2541                return Err(Box::new(ValidationError {
2542                    problem: "this device is a portability subset device, and \
2543                        `flags` contains `ImageCreateFlags::DIM2D_ARRAY_COMPATIBLE`"
2544                        .into(),
2545                    requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceFeature(
2546                        "image_view2_d_on3_d_image",
2547                    )])]),
2548                    vuids: &["VUID-VkImageCreateInfo-imageView2DOn3DImage-04459"],
2549                    ..Default::default()
2550                }));
2551            }
2552        }
2553
2554        if flags.intersects(ImageCreateFlags::DIM2D_VIEW_COMPATIBLE) {
2555            if image_type != ImageType::Dim3d {
2556                return Err(Box::new(ValidationError {
2557                    problem: "`flags` contains `ImageCreateFlags::DIM2D_VIEW_COMPATIBLE`, but \
2558                        `image_type` is not `ImageType::Dim3d`"
2559                        .into(),
2560                    vuids: &["VUID-VkImageCreateInfo-flags-07755"],
2561                    ..Default::default()
2562                }));
2563            }
2564        }
2565
2566        if flags.intersects(ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE)
2567            && format.compression().is_none()
2568        {
2569            return Err(Box::new(ValidationError {
2570                problem: "`flags` contains `ImageCreateFlags::BLOCK_TEXEL_VIEW_COMPATIBLE`, \
2571                    but `format` is not a compressed format"
2572                    .into(),
2573                vuids: &["VUID-VkImageCreateInfo-flags-01572"],
2574                ..Default::default()
2575            }));
2576        }
2577
2578        if flags.intersects(ImageCreateFlags::DISJOINT) {
2579            if format.planes().len() < 2 {
2580                return Err(Box::new(ValidationError {
2581                    problem: "`flags` contains `ImageCreateFlags::DISJOINT`, but `format` \
2582                        is not a multi-planar format"
2583                        .into(),
2584                    vuids: &["VUID-VkImageCreateInfo-format-01577"],
2585                    ..Default::default()
2586                }));
2587            }
2588
2589            if !image_create_format_features.intersects(FormatFeatures::DISJOINT) {
2590                return Err(Box::new(ValidationError {
2591                    problem: "`flags` contains `ImageCreateFlags::DISJOINT`, but the \
2592                        format features of `format` do not include \
2593                        `FormatFeatures::DISJOINT`"
2594                        .into(),
2595                    vuids: &["VUID-VkImageCreateInfo-imageCreateFormatFeatures-02260"],
2596                    ..Default::default()
2597                }));
2598            }
2599        }
2600
2601        /* Check sharing mode and queue families */
2602
2603        match sharing {
2604            Sharing::Exclusive => (),
2605            Sharing::Concurrent(queue_family_indices) => {
2606                if queue_family_indices.len() < 2 {
2607                    return Err(Box::new(ValidationError {
2608                        context: "sharing".into(),
2609                        problem: "is `Sharing::Concurrent`, but contains less than 2 elements"
2610                            .into(),
2611                        vuids: &["VUID-VkImageCreateInfo-sharingMode-00942"],
2612                        ..Default::default()
2613                    }));
2614                }
2615
2616                let queue_family_count = physical_device.queue_family_properties().len() as u32;
2617
2618                for (index, &queue_family_index) in queue_family_indices.iter().enumerate() {
2619                    if queue_family_indices[..index].contains(&queue_family_index) {
2620                        return Err(Box::new(ValidationError {
2621                            context: "queue_family_indices".into(),
2622                            problem: format!(
2623                                "the queue family index in the list at index {} is contained in \
2624                                the list more than once",
2625                                index,
2626                            )
2627                            .into(),
2628                            vuids: &["VUID-VkImageCreateInfo-sharingMode-01420"],
2629                            ..Default::default()
2630                        }));
2631                    }
2632
2633                    if queue_family_index >= queue_family_count {
2634                        return Err(Box::new(ValidationError {
2635                            context: format!("sharing[{}]", index).into(),
2636                            problem: "is not less than the number of queue families in the device"
2637                                .into(),
2638                            vuids: &["VUID-VkImageCreateInfo-sharingMode-01420"],
2639                            ..Default::default()
2640                        }));
2641                    }
2642                }
2643            }
2644        }
2645
2646        if !(drm_format_modifier_plane_layouts.is_empty() || drm_format_modifiers.len() == 1) {
2647            return Err(Box::new(ValidationError {
2648                problem: "`drm_format_modifier_plane_layouts` is not empty, but \
2649                    `drm_format_modifiers` does not contain exactly one element"
2650                    .into(),
2651                ..Default::default()
2652            }));
2653        }
2654
2655        match (tiling, !drm_format_modifiers.is_empty()) {
2656            (ImageTiling::DrmFormatModifier, true) => {
2657                if flags.intersects(ImageCreateFlags::MUTABLE_FORMAT) && view_formats.is_empty() {
2658                    return Err(Box::new(ValidationError {
2659                        problem: "`tiling` is `ImageTiling::DrmFormatModifier`, and \
2660                            `flags` contains `ImageCreateFlags::MUTABLE_FORMAT`, but \
2661                            `view_formats` is empty"
2662                            .into(),
2663                        vuids: &["VUID-VkImageCreateInfo-tiling-02353"],
2664                        ..Default::default()
2665                    }));
2666                }
2667
2668                if !drm_format_modifier_plane_layouts.is_empty() {
2669                    let drm_format_modifier = drm_format_modifiers[0];
2670                    let drm_format_modifier_properties = format_properties
2671                        .drm_format_modifier_properties
2672                        .iter()
2673                        .find(|properties| properties.drm_format_modifier == drm_format_modifier)
2674                        .ok_or_else(|| Box::new(ValidationError {
2675                            problem: "`drm_format_modifier_plane_layouts` is not empty, but \
2676                                `drm_format_modifiers[0]` is not one of the modifiers in \
2677                                `FormatProperties::drm_format_properties`, as returned by \
2678                                `PhysicalDevice::format_properties` for `format`".into(),
2679                            vuids: &["VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-drmFormatModifierPlaneCount-02265"],
2680                            ..Default::default()
2681                        }))?;
2682
2683                    if drm_format_modifier_plane_layouts.len()
2684                        != drm_format_modifier_properties.drm_format_modifier_plane_count as usize
2685                    {
2686                        return Err(Box::new(ValidationError {
2687                            problem: "`drm_format_modifier_plane_layouts` is not empty, but the \
2688                                number of provided subresource layouts does not equal the number \
2689                                of memory planes for `drm_format_modifiers[0]`, specified in \
2690                                `DrmFormatModifierProperties::drm_format_modifier_plane_count`, \
2691                                as returned by `PhysicalDevice::format_properties` for `format`"
2692                                .into(),
2693                            vuids: &["VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-drmFormatModifierPlaneCount-02265"],
2694                            ..Default::default()
2695                        }));
2696                    }
2697
2698                    for (index, subresource_layout) in
2699                        drm_format_modifier_plane_layouts.iter().enumerate()
2700                    {
2701                        let &SubresourceLayout {
2702                            offset: _,
2703                            size,
2704                            row_pitch: _,
2705                            array_pitch,
2706                            depth_pitch,
2707                        } = subresource_layout;
2708
2709                        if size != 0 {
2710                            return Err(Box::new(ValidationError {
2711                                context: format!(
2712                                    "drm_format_modifier_plane_layouts[{}].size",
2713                                    index
2714                                )
2715                                .into(),
2716                                problem: "is not zero".into(),
2717                                vuids: &[
2718                                    "VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-size-02267",
2719                                ],
2720                                ..Default::default()
2721                            }));
2722                        }
2723
2724                        if array_layers == 1 && array_pitch.is_some() {
2725                            return Err(Box::new(ValidationError {
2726                                problem: format!(
2727                                    "`array_layers` is 1, but \
2728                                    `drm_format_modifier_plane_layouts[{}].array_pitch` is `Some`",
2729                                    index
2730                                )
2731                                .into(),
2732                                vuids: &["VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-arrayPitch-02268"],
2733                                ..Default::default()
2734                            }));
2735                        }
2736
2737                        if extent[2] == 1 && depth_pitch.is_some() {
2738                            return Err(Box::new(ValidationError {
2739                                problem: format!(
2740                                    "`extent[2]` is 1, but \
2741                                    `drm_format_modifier_plane_layouts[{}].depth_pitch` is `Some`",
2742                                    index
2743                                )
2744                                .into(),
2745                                vuids: &["VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-depthPitch-02269"],
2746                                ..Default::default()
2747                            }));
2748                        }
2749                    }
2750                }
2751            }
2752            (ImageTiling::DrmFormatModifier, false) => {
2753                return Err(Box::new(ValidationError {
2754                    problem: "`tiling` is `ImageTiling::DrmFormatModifier`, but \
2755                        `drm_format_modifiers` is empty"
2756                        .into(),
2757                    vuids: &["VUID-VkImageCreateInfo-tiling-02261"],
2758                    ..Default::default()
2759                }));
2760            }
2761            (_, true) => {
2762                if tiling != ImageTiling::DrmFormatModifier {
2763                    return Err(Box::new(ValidationError {
2764                        problem: "`drm_format_modifiers` is not empty, but \
2765                            `tiling` is not `ImageTiling::DrmFormatModifier`"
2766                            .into(),
2767                        vuids: &["VUID-VkImageCreateInfo-pNext-02262"],
2768                        ..Default::default()
2769                    }));
2770                }
2771            }
2772            (_, false) => (),
2773        }
2774
2775        if !external_memory_handle_types.is_empty() {
2776            if !(device.api_version() >= Version::V1_1
2777                || device.enabled_extensions().khr_external_memory)
2778            {
2779                return Err(Box::new(ValidationError {
2780                    context: "external_memory_handle_types".into(),
2781                    problem: "is not empty".into(),
2782                    requires_one_of: RequiresOneOf(&[
2783                        RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]),
2784                        RequiresAllOf(&[Requires::DeviceExtension("khr_external_memory")]),
2785                    ]),
2786                    ..Default::default()
2787                }));
2788            }
2789
2790            external_memory_handle_types
2791                .validate_device(device)
2792                .map_err(|err| {
2793                    err.add_context("external_memory_handle_types")
2794                        .set_vuids(&["VUID-VkExternalMemoryImageCreateInfo-handleTypes-parameter"])
2795                })?;
2796
2797            if initial_layout != ImageLayout::Undefined {
2798                return Err(Box::new(ValidationError {
2799                    problem: "`external_memory_handle_types` is not empty, but \
2800                        `initial_layout` is not `ImageLayout::Undefined`"
2801                        .into(),
2802                    vuids: &["VUID-VkImageCreateInfo-pNext-01443"],
2803                    ..Default::default()
2804                }));
2805            }
2806        }
2807
2808        /*
2809            Some device limits can be exceeded, but only for particular image configurations, which
2810            must be queried with `image_format_properties`. See:
2811            https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap48.html#capabilities-image
2812            First, we check if this is the case, then query the device if so.
2813        */
2814
2815        fn iter_or_none<T>(iter: impl IntoIterator<Item = T>) -> impl Iterator<Item = Option<T>> {
2816            let mut iter = iter.into_iter();
2817            [iter.next()].into_iter().chain(iter.map(Some))
2818        }
2819
2820        for drm_format_modifier in iter_or_none(drm_format_modifiers.iter().copied()) {
2821            for external_memory_handle_type in iter_or_none(external_memory_handle_types) {
2822                let image_format_properties = unsafe {
2823                    physical_device.image_format_properties_unchecked(ImageFormatInfo {
2824                        flags,
2825                        format,
2826                        image_type,
2827                        tiling,
2828                        usage,
2829                        stencil_usage,
2830                        external_memory_handle_type,
2831                        drm_format_modifier_info: drm_format_modifier.map(|drm_format_modifier| {
2832                            ImageDrmFormatModifierInfo {
2833                                drm_format_modifier,
2834                                sharing: sharing.clone(),
2835                                ..Default::default()
2836                            }
2837                        }),
2838                        ..Default::default()
2839                    })
2840                }
2841                .map_err(|_err| {
2842                    Box::new(ValidationError {
2843                        problem: "`PhysicalDevice::image_format_properties` \
2844                                    returned an error"
2845                            .into(),
2846                        ..Default::default()
2847                    })
2848                })?;
2849
2850                let image_format_properties = image_format_properties.ok_or_else(|| Box::new(ValidationError {
2851                    problem: "the combination of parameters of this image is not \
2852                        supported by the physical device, as returned by \
2853                        `PhysicalDevice::image_format_properties`"
2854                        .into(),
2855                    vuids: &[
2856                        "VUID-VkImageDrmFormatModifierExplicitCreateInfoEXT-drmFormatModifier-02264",
2857                        "VUID-VkImageDrmFormatModifierListCreateInfoEXT-pDrmFormatModifiers-02263",
2858                    ],
2859                    ..Default::default()
2860                }))?;
2861
2862                let ImageFormatProperties {
2863                    max_extent,
2864                    max_mip_levels,
2865                    max_array_layers,
2866                    sample_counts,
2867                    max_resource_size: _,
2868                    external_memory_properties,
2869                    filter_cubic: _,
2870                    filter_cubic_minmax: _,
2871                } = image_format_properties;
2872
2873                if extent[0] > max_extent[0] {
2874                    return Err(Box::new(ValidationError {
2875                        problem: "`extent[0]` exceeds `max_extent[0]` for \
2876                            the combination of parameters of this image, as returned by \
2877                            `PhysicalDevice::image_format_properties`"
2878                            .into(),
2879                        vuids: &["VUID-VkImageCreateInfo-extent-02252"],
2880                        ..Default::default()
2881                    }));
2882                }
2883
2884                if extent[1] > max_extent[1] {
2885                    return Err(Box::new(ValidationError {
2886                        problem: "`extent[1]` exceeds `max_extent[1]` for \
2887                            the combination of parameters of this image, as returned by \
2888                            `PhysicalDevice::image_format_properties`"
2889                            .into(),
2890                        vuids: &["VUID-VkImageCreateInfo-extent-02253"],
2891                        ..Default::default()
2892                    }));
2893                }
2894
2895                if extent[2] > max_extent[2] {
2896                    return Err(Box::new(ValidationError {
2897                        problem: "`extent[2]` exceeds `max_extent[2]` for \
2898                            the combination of parameters of this image, as returned by \
2899                            `PhysicalDevice::image_format_properties`"
2900                            .into(),
2901                        vuids: &["VUID-VkImageCreateInfo-extent-02254"],
2902                        ..Default::default()
2903                    }));
2904                }
2905
2906                if mip_levels > max_mip_levels {
2907                    return Err(Box::new(ValidationError {
2908                        problem: "`mip_levels` exceeds `max_mip_levels` for \
2909                            the combination of parameters of this image, as returned by \
2910                            `PhysicalDevice::image_format_properties`"
2911                            .into(),
2912                        vuids: &["VUID-VkImageCreateInfo-mipLevels-02255"],
2913                        ..Default::default()
2914                    }));
2915                }
2916
2917                if array_layers > max_array_layers {
2918                    return Err(Box::new(ValidationError {
2919                        problem: "`array_layers` exceeds `max_array_layers` for \
2920                            the combination of parameters of this image, as returned by \
2921                            `PhysicalDevice::image_format_properties`"
2922                            .into(),
2923                        vuids: &["VUID-VkImageCreateInfo-arrayLayers-02256"],
2924                        ..Default::default()
2925                    }));
2926                }
2927
2928                if !sample_counts.contains_enum(samples) {
2929                    return Err(Box::new(ValidationError {
2930                        problem: "`samples` is not present in the `sample_counts` value for \
2931                            the combination of parameters of this image, as returned by \
2932                            `PhysicalDevice::image_format_properties`"
2933                            .into(),
2934                        vuids: &["VUID-VkImageCreateInfo-samples-02258"],
2935                        ..Default::default()
2936                    }));
2937                }
2938
2939                if !external_memory_properties
2940                    .compatible_handle_types
2941                    .contains(external_memory_handle_types)
2942                {
2943                    return Err(Box::new(ValidationError {
2944                        problem: "`external_memory_handle_types` is not a subset of the \
2945                            `external_memory_properties.compatible_handle_types` value for \
2946                            the combination of parameters of this image, as returned by \
2947                            `PhysicalDevice::image_format_properties`"
2948                            .into(),
2949                        vuids: &["VUID-VkImageCreateInfo-pNext-00990"],
2950                        ..Default::default()
2951                    }));
2952                }
2953            }
2954        }
2955
2956        Ok(())
2957    }
2958
2959    pub(crate) fn to_vk<'a>(
2960        &'a self,
2961        extensions_vk: &'a mut ImageCreateInfoExtensionsVk<'_>,
2962    ) -> ash::vk::ImageCreateInfo<'a> {
2963        let &Self {
2964            flags,
2965            image_type,
2966            format,
2967            view_formats: _,
2968            extent,
2969            array_layers,
2970            mip_levels,
2971            samples,
2972            tiling,
2973            usage,
2974            stencil_usage: _,
2975            ref sharing,
2976            initial_layout,
2977            drm_format_modifiers: _,
2978            drm_format_modifier_plane_layouts: _,
2979            external_memory_handle_types: _,
2980            _ne: _,
2981        } = self;
2982
2983        let (sharing_mode, queue_family_indices_vk) = match sharing {
2984            Sharing::Exclusive => (ash::vk::SharingMode::EXCLUSIVE, [].as_slice()),
2985            Sharing::Concurrent(queue_family_indices) => (
2986                ash::vk::SharingMode::CONCURRENT,
2987                queue_family_indices.as_slice(),
2988            ),
2989        };
2990
2991        let mut val_vk = ash::vk::ImageCreateInfo::default()
2992            .flags(flags.into())
2993            .image_type(image_type.into())
2994            .format(format.into())
2995            .extent(ash::vk::Extent3D {
2996                width: extent[0],
2997                height: extent[1],
2998                depth: extent[2],
2999            })
3000            .mip_levels(mip_levels)
3001            .array_layers(array_layers)
3002            .samples(samples.into())
3003            .tiling(tiling.into())
3004            .usage(usage.into())
3005            .sharing_mode(sharing_mode)
3006            .queue_family_indices(queue_family_indices_vk)
3007            .initial_layout(initial_layout.into());
3008
3009        let ImageCreateInfoExtensionsVk {
3010            drm_format_modifier_explicit_vk,
3011            drm_format_modifier_list_vk,
3012            external_memory_vk,
3013            format_list_vk,
3014            stencil_usage_vk,
3015        } = extensions_vk;
3016
3017        if let Some(next) = drm_format_modifier_explicit_vk {
3018            val_vk = val_vk.push_next(next);
3019        }
3020
3021        if let Some(next) = drm_format_modifier_list_vk {
3022            val_vk = val_vk.push_next(next);
3023        }
3024
3025        if let Some(next) = external_memory_vk {
3026            val_vk = val_vk.push_next(next);
3027        }
3028
3029        if let Some(next) = format_list_vk {
3030            val_vk = val_vk.push_next(next);
3031        }
3032
3033        if let Some(next) = stencil_usage_vk {
3034            val_vk = val_vk.push_next(next);
3035        }
3036
3037        val_vk
3038    }
3039
3040    pub(crate) fn to_vk_extensions<'a>(
3041        &'a self,
3042        fields1_vk: &'a ImageCreateInfoFields1Vk,
3043    ) -> ImageCreateInfoExtensionsVk<'a> {
3044        let ImageCreateInfoFields1Vk {
3045            plane_layouts_vk,
3046            view_formats_vk,
3047        } = fields1_vk;
3048
3049        let drm_format_modifier_explicit_vk = (!plane_layouts_vk.is_empty()).then(|| {
3050            ash::vk::ImageDrmFormatModifierExplicitCreateInfoEXT::default()
3051                .drm_format_modifier(self.drm_format_modifiers[0])
3052                .plane_layouts(plane_layouts_vk)
3053        });
3054
3055        let drm_format_modifier_list_vk = (!self.drm_format_modifier_plane_layouts.is_empty())
3056            .then(|| {
3057                ash::vk::ImageDrmFormatModifierListCreateInfoEXT::default()
3058                    .drm_format_modifiers(&self.drm_format_modifiers)
3059            });
3060
3061        let external_memory_vk = (!self.external_memory_handle_types.is_empty()).then(|| {
3062            ash::vk::ExternalMemoryImageCreateInfo::default()
3063                .handle_types(self.external_memory_handle_types.into())
3064        });
3065
3066        let format_list_vk = (!view_formats_vk.is_empty())
3067            .then(|| ash::vk::ImageFormatListCreateInfo::default().view_formats(view_formats_vk));
3068
3069        let stencil_usage_vk = self.stencil_usage.map(|stencil_usage| {
3070            ash::vk::ImageStencilUsageCreateInfo::default().stencil_usage(stencil_usage.into())
3071        });
3072
3073        ImageCreateInfoExtensionsVk {
3074            drm_format_modifier_explicit_vk,
3075            drm_format_modifier_list_vk,
3076            external_memory_vk,
3077            format_list_vk,
3078            stencil_usage_vk,
3079        }
3080    }
3081
3082    pub(crate) fn to_vk_fields1(&self) -> ImageCreateInfoFields1Vk {
3083        let plane_layouts_vk = self
3084            .drm_format_modifier_plane_layouts
3085            .iter()
3086            .map(SubresourceLayout::to_vk)
3087            .collect();
3088
3089        let view_formats_vk = self
3090            .view_formats
3091            .iter()
3092            .copied()
3093            .map(ash::vk::Format::from)
3094            .collect();
3095
3096        ImageCreateInfoFields1Vk {
3097            plane_layouts_vk,
3098            view_formats_vk,
3099        }
3100    }
3101}
3102
3103pub(crate) struct ImageCreateInfoExtensionsVk<'a> {
3104    pub(crate) drm_format_modifier_explicit_vk:
3105        Option<ash::vk::ImageDrmFormatModifierExplicitCreateInfoEXT<'a>>,
3106    pub(crate) drm_format_modifier_list_vk:
3107        Option<ash::vk::ImageDrmFormatModifierListCreateInfoEXT<'a>>,
3108    pub(crate) external_memory_vk: Option<ash::vk::ExternalMemoryImageCreateInfo<'static>>,
3109    pub(crate) format_list_vk: Option<ash::vk::ImageFormatListCreateInfo<'a>>,
3110    pub(crate) stencil_usage_vk: Option<ash::vk::ImageStencilUsageCreateInfo<'static>>,
3111}
3112
3113pub(crate) struct ImageCreateInfoFields1Vk {
3114    plane_layouts_vk: SmallVec<[ash::vk::SubresourceLayout; 4]>,
3115    view_formats_vk: Vec<ash::vk::Format>,
3116}
3117
3118#[cfg(test)]
3119mod tests {
3120    use super::{ImageCreateInfo, ImageUsage, RawImage};
3121    use crate::{
3122        format::Format,
3123        image::{
3124            ImageAspect, ImageAspects, ImageCreateFlags, ImageSubresourceRange, ImageType,
3125            SampleCount, SubresourceRangeIterator,
3126        },
3127        DeviceSize, Requires, RequiresAllOf, RequiresOneOf, Validated, ValidationError,
3128    };
3129    use smallvec::SmallVec;
3130
3131    #[test]
3132    fn create_sampled() {
3133        let (device, _) = gfx_dev_and_queue!();
3134
3135        let _ = RawImage::new(
3136            device,
3137            ImageCreateInfo {
3138                image_type: ImageType::Dim2d,
3139                format: Format::R8G8B8A8_UNORM,
3140                extent: [32, 32, 1],
3141                usage: ImageUsage::SAMPLED,
3142                ..Default::default()
3143            },
3144        )
3145        .unwrap();
3146    }
3147
3148    #[test]
3149    fn create_transient() {
3150        let (device, _) = gfx_dev_and_queue!();
3151
3152        let _ = RawImage::new(
3153            device,
3154            ImageCreateInfo {
3155                image_type: ImageType::Dim2d,
3156                format: Format::R8G8B8A8_UNORM,
3157                extent: [32, 32, 1],
3158                usage: ImageUsage::TRANSIENT_ATTACHMENT | ImageUsage::COLOR_ATTACHMENT,
3159                ..Default::default()
3160            },
3161        )
3162        .unwrap();
3163    }
3164
3165    #[test]
3166    fn zero_mipmap() {
3167        let (device, _) = gfx_dev_and_queue!();
3168
3169        assert!(matches!(
3170            RawImage::new(
3171                device,
3172                ImageCreateInfo {
3173                    image_type: ImageType::Dim2d,
3174                    format: Format::R8G8B8A8_UNORM,
3175                    extent: [32, 32, 1],
3176                    mip_levels: 0,
3177                    usage: ImageUsage::SAMPLED,
3178                    ..Default::default()
3179                },
3180            ),
3181            Err(Validated::ValidationError(_))
3182        ),);
3183    }
3184
3185    #[test]
3186    fn mipmaps_too_high() {
3187        let (device, _) = gfx_dev_and_queue!();
3188
3189        let res = RawImage::new(
3190            device,
3191            ImageCreateInfo {
3192                image_type: ImageType::Dim2d,
3193                format: Format::R8G8B8A8_UNORM,
3194                extent: [32, 32, 1],
3195                mip_levels: u32::MAX,
3196                usage: ImageUsage::SAMPLED,
3197                ..Default::default()
3198            },
3199        );
3200
3201        match res {
3202            Err(Validated::ValidationError(_)) => (),
3203            _ => panic!(),
3204        };
3205    }
3206
3207    #[test]
3208    fn shader_storage_image_multisample() {
3209        let (device, _) = gfx_dev_and_queue!();
3210
3211        let res = RawImage::new(
3212            device,
3213            ImageCreateInfo {
3214                image_type: ImageType::Dim2d,
3215                format: Format::R8G8B8A8_UNORM,
3216                extent: [32, 32, 1],
3217                samples: SampleCount::Sample2,
3218                usage: ImageUsage::STORAGE,
3219                ..Default::default()
3220            },
3221        );
3222
3223        match res {
3224            Err(Validated::ValidationError(err))
3225                if matches!(
3226                    *err,
3227                    ValidationError {
3228                        requires_one_of: RequiresOneOf([RequiresAllOf([Requires::DeviceFeature(
3229                            "shader_storage_image_multisample"
3230                        )])],),
3231                        ..
3232                    }
3233                ) => {}
3234            Err(Validated::ValidationError(_)) => (), // unlikely but possible
3235            _ => panic!(),
3236        };
3237    }
3238
3239    #[test]
3240    fn compressed_not_color_attachment() {
3241        let (device, _) = gfx_dev_and_queue!();
3242
3243        let res = RawImage::new(
3244            device,
3245            ImageCreateInfo {
3246                image_type: ImageType::Dim2d,
3247                format: Format::ASTC_5x4_UNORM_BLOCK,
3248                extent: [32, 32, 1],
3249                usage: ImageUsage::COLOR_ATTACHMENT,
3250                ..Default::default()
3251            },
3252        );
3253
3254        match res {
3255            Err(Validated::ValidationError(_)) => (),
3256            _ => panic!(),
3257        };
3258    }
3259
3260    #[test]
3261    fn transient_forbidden_with_some_usages() {
3262        let (device, _) = gfx_dev_and_queue!();
3263
3264        assert!(matches!(
3265            RawImage::new(
3266                device,
3267                ImageCreateInfo {
3268                    image_type: ImageType::Dim2d,
3269                    format: Format::R8G8B8A8_UNORM,
3270                    extent: [32, 32, 1],
3271                    usage: ImageUsage::TRANSIENT_ATTACHMENT | ImageUsage::SAMPLED,
3272                    ..Default::default()
3273                },
3274            ),
3275            Err(Validated::ValidationError(_))
3276        ))
3277    }
3278
3279    #[test]
3280    fn cubecompatible_dims_mismatch() {
3281        let (device, _) = gfx_dev_and_queue!();
3282
3283        let res = RawImage::new(
3284            device,
3285            ImageCreateInfo {
3286                flags: ImageCreateFlags::CUBE_COMPATIBLE,
3287                image_type: ImageType::Dim2d,
3288                format: Format::R8G8B8A8_UNORM,
3289                extent: [32, 64, 1],
3290                usage: ImageUsage::SAMPLED,
3291                ..Default::default()
3292            },
3293        );
3294
3295        match res {
3296            Err(Validated::ValidationError(_)) => (),
3297            _ => panic!(),
3298        };
3299    }
3300
3301    #[test]
3302    #[allow(clippy::erasing_op, clippy::identity_op)]
3303    fn subresource_range_iterator() {
3304        // A fictitious set of aspects that no real image would actually ever have.
3305        let image_aspect_list: SmallVec<[ImageAspect; 4]> = (ImageAspects::COLOR
3306            | ImageAspects::DEPTH
3307            | ImageAspects::STENCIL
3308            | ImageAspects::PLANE_0)
3309            .into_iter()
3310            .collect();
3311        let image_mip_levels = 6;
3312        let image_array_layers = 8;
3313
3314        let mip = image_array_layers as DeviceSize;
3315        let asp = mip * image_mip_levels as DeviceSize;
3316
3317        // Whole image
3318        let mut iter = SubresourceRangeIterator::new(
3319            ImageSubresourceRange {
3320                aspects: ImageAspects::COLOR
3321                    | ImageAspects::DEPTH
3322                    | ImageAspects::STENCIL
3323                    | ImageAspects::PLANE_0,
3324                mip_levels: 0..6,
3325                array_layers: 0..8,
3326            },
3327            &image_aspect_list,
3328            asp,
3329            image_mip_levels,
3330            mip,
3331            image_array_layers,
3332        );
3333
3334        assert_eq!(iter.next(), Some(0 * asp..4 * asp));
3335        assert_eq!(iter.next(), None);
3336
3337        // Only some aspects
3338        let mut iter = SubresourceRangeIterator::new(
3339            ImageSubresourceRange {
3340                aspects: ImageAspects::COLOR | ImageAspects::DEPTH | ImageAspects::PLANE_0,
3341                mip_levels: 0..6,
3342                array_layers: 0..8,
3343            },
3344            &image_aspect_list,
3345            asp,
3346            image_mip_levels,
3347            mip,
3348            image_array_layers,
3349        );
3350
3351        assert_eq!(iter.next(), Some(0 * asp..2 * asp));
3352        assert_eq!(iter.next(), Some(3 * asp..4 * asp));
3353        assert_eq!(iter.next(), None);
3354
3355        // Two aspects, and only some of the mip levels
3356        let mut iter = SubresourceRangeIterator::new(
3357            ImageSubresourceRange {
3358                aspects: ImageAspects::DEPTH | ImageAspects::STENCIL,
3359                mip_levels: 2..4,
3360                array_layers: 0..8,
3361            },
3362            &image_aspect_list,
3363            asp,
3364            image_mip_levels,
3365            mip,
3366            image_array_layers,
3367        );
3368        assert_eq!(iter.next(), Some(1 * asp + 2 * mip..1 * asp + 4 * mip));
3369        assert_eq!(iter.next(), Some(2 * asp + 2 * mip..2 * asp + 4 * mip));
3370        assert_eq!(iter.next(), None);
3371
3372        // One aspect, one mip level, only some of the array layers
3373        let mut iter = SubresourceRangeIterator::new(
3374            ImageSubresourceRange {
3375                aspects: ImageAspects::COLOR,
3376
3377                mip_levels: 0..1,
3378                array_layers: 2..4,
3379            },
3380            &image_aspect_list,
3381            asp,
3382            image_mip_levels,
3383            mip,
3384            image_array_layers,
3385        );
3386
3387        assert_eq!(
3388            iter.next(),
3389            Some(0 * asp + 0 * mip + 2..0 * asp + 0 * mip + 4)
3390        );
3391        assert_eq!(iter.next(), None);
3392
3393        // Two aspects, two mip levels, only some of the array layers
3394        let mut iter = SubresourceRangeIterator::new(
3395            ImageSubresourceRange {
3396                aspects: ImageAspects::DEPTH | ImageAspects::STENCIL,
3397                mip_levels: 2..4,
3398                array_layers: 6..8,
3399            },
3400            &image_aspect_list,
3401            asp,
3402            image_mip_levels,
3403            mip,
3404            image_array_layers,
3405        );
3406        assert_eq!(
3407            iter.next(),
3408            Some(1 * asp + 2 * mip + 6..1 * asp + 2 * mip + 8)
3409        );
3410        assert_eq!(
3411            iter.next(),
3412            Some(1 * asp + 3 * mip + 6..1 * asp + 3 * mip + 8)
3413        );
3414        assert_eq!(
3415            iter.next(),
3416            Some(2 * asp + 2 * mip + 6..2 * asp + 2 * mip + 8)
3417        );
3418        assert_eq!(
3419            iter.next(),
3420            Some(2 * asp + 3 * mip + 6..2 * asp + 3 * mip + 8)
3421        );
3422        assert_eq!(iter.next(), None);
3423    }
3424}