• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2022 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 use super::{
11     BlitImageInfo, BufferCopy, BufferImageCopy, CommandBufferBuilder, CopyBufferInfo,
12     CopyBufferToImageInfo, CopyError, CopyErrorResource, CopyImageInfo, CopyImageToBufferInfo,
13     ImageCopy, ResolveImageInfo,
14 };
15 use crate::{
16     buffer::BufferUsage,
17     command_buffer::{
18         allocator::CommandBufferAllocator, ImageBlit, ImageResolve, ResourceInCommand,
19         ResourceUseRef,
20     },
21     device::{DeviceOwned, QueueFlags},
22     format::{Format, FormatFeatures, NumericType},
23     image::{
24         ImageAccess, ImageAspects, ImageDimensions, ImageLayout, ImageSubresourceLayers,
25         ImageSubresourceRange, ImageType, ImageUsage, SampleCount, SampleCounts,
26     },
27     sampler::Filter,
28     sync::PipelineStageAccess,
29     DeviceSize, Version, VulkanObject,
30 };
31 use smallvec::SmallVec;
32 use std::cmp::{max, min};
33 
34 impl<L, A> CommandBufferBuilder<L, A>
35 where
36     A: CommandBufferAllocator,
37 {
38     /// Copies data from a buffer to another buffer.
39     ///
40     /// # Panics
41     ///
42     /// - Panics if `src_buffer` or `dst_buffer` were not created from the same device
43     ///   as `self`.
44     ///
45     /// # Safety
46     ///
47     /// - Appropriate synchronization must be provided for all buffers
48     ///   that are accessed by the command.
49     #[inline]
copy_buffer( &mut self, copy_buffer_info: impl Into<CopyBufferInfo>, ) -> Result<&mut Self, CopyError>50     pub unsafe fn copy_buffer(
51         &mut self,
52         copy_buffer_info: impl Into<CopyBufferInfo>,
53     ) -> Result<&mut Self, CopyError> {
54         let copy_buffer_info = copy_buffer_info.into();
55         self.validate_copy_buffer(&copy_buffer_info)?;
56 
57         unsafe { Ok(self.copy_buffer_unchecked(copy_buffer_info)) }
58     }
59 
validate_copy_buffer(&self, copy_buffer_info: &CopyBufferInfo) -> Result<(), CopyError>60     fn validate_copy_buffer(&self, copy_buffer_info: &CopyBufferInfo) -> Result<(), CopyError> {
61         let device = self.device();
62 
63         // VUID-vkCmdCopyBuffer2-renderpass
64         if self.builder_state.render_pass.is_some() {
65             return Err(CopyError::ForbiddenInsideRenderPass);
66         }
67 
68         let queue_family_properties = self.queue_family_properties();
69 
70         // VUID-vkCmdCopyBuffer2-commandBuffer-cmdpool
71         if !queue_family_properties
72             .queue_flags
73             .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
74         {
75             return Err(CopyError::NotSupportedByQueueFamily);
76         }
77 
78         let &CopyBufferInfo {
79             ref src_buffer,
80             ref dst_buffer,
81             ref regions,
82             _ne: _,
83         } = copy_buffer_info;
84 
85         // VUID-VkCopyBufferInfo2-commonparent
86         assert_eq!(device, src_buffer.device());
87         assert_eq!(device, dst_buffer.device());
88 
89         // VUID-VkCopyBufferInfo2-srcBuffer-00118
90         if !src_buffer
91             .buffer()
92             .usage()
93             .intersects(BufferUsage::TRANSFER_SRC)
94         {
95             return Err(CopyError::MissingUsage {
96                 resource: CopyErrorResource::Source,
97                 usage: "transfer_src",
98             });
99         }
100 
101         // VUID-VkCopyBufferInfo2-dstBuffer-00120
102         if !dst_buffer
103             .buffer()
104             .usage()
105             .intersects(BufferUsage::TRANSFER_DST)
106         {
107             return Err(CopyError::MissingUsage {
108                 resource: CopyErrorResource::Destination,
109                 usage: "transfer_dst",
110             });
111         }
112 
113         let same_buffer = src_buffer.buffer() == dst_buffer.buffer();
114         let mut overlap_indices = None;
115 
116         for (region_index, region) in regions.iter().enumerate() {
117             let &BufferCopy {
118                 src_offset,
119                 dst_offset,
120                 size,
121                 _ne: _,
122             } = region;
123 
124             // VUID-VkBufferCopy2-size-01988
125             assert!(size != 0);
126 
127             // VUID-VkCopyBufferInfo2-srcOffset-00113
128             // VUID-VkCopyBufferInfo2-size-00115
129             if src_offset + size > src_buffer.size() {
130                 return Err(CopyError::RegionOutOfBufferBounds {
131                     resource: CopyErrorResource::Source,
132                     region_index,
133                     offset_range_end: src_offset + size,
134                     buffer_size: src_buffer.size(),
135                 });
136             }
137 
138             // VUID-VkCopyBufferInfo2-dstOffset-00114
139             // VUID-VkCopyBufferInfo2-size-00116
140             if dst_offset + size > dst_buffer.size() {
141                 return Err(CopyError::RegionOutOfBufferBounds {
142                     resource: CopyErrorResource::Destination,
143                     region_index,
144                     offset_range_end: dst_offset + size,
145                     buffer_size: dst_buffer.size(),
146                 });
147             }
148 
149             // VUID-VkCopyBufferInfo2-pRegions-00117
150             if same_buffer {
151                 let src_region_index = region_index;
152                 let src_range =
153                     src_buffer.offset() + src_offset..src_buffer.offset() + src_offset + size;
154 
155                 for (dst_region_index, dst_region) in regions.iter().enumerate() {
156                     let &BufferCopy { dst_offset, .. } = dst_region;
157 
158                     let dst_range =
159                         dst_buffer.offset() + dst_offset..dst_buffer.offset() + dst_offset + size;
160 
161                     if src_range.start >= dst_range.end || dst_range.start >= src_range.end {
162                         // The regions do not overlap
163                         continue;
164                     }
165 
166                     overlap_indices = Some((src_region_index, dst_region_index));
167                 }
168             }
169         }
170 
171         // VUID-VkCopyBufferInfo2-pRegions-00117
172         if let Some((src_region_index, dst_region_index)) = overlap_indices {
173             return Err(CopyError::OverlappingRegions {
174                 src_region_index,
175                 dst_region_index,
176             });
177         }
178 
179         // TODO: sync check
180 
181         Ok(())
182     }
183 
184     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
copy_buffer_unchecked( &mut self, copy_buffer_info: impl Into<CopyBufferInfo>, ) -> &mut Self185     pub unsafe fn copy_buffer_unchecked(
186         &mut self,
187         copy_buffer_info: impl Into<CopyBufferInfo>,
188     ) -> &mut Self {
189         let copy_buffer_info = copy_buffer_info.into();
190         let CopyBufferInfo {
191             src_buffer,
192             dst_buffer,
193             regions,
194             _ne: _,
195         } = copy_buffer_info;
196 
197         if regions.is_empty() {
198             return self;
199         }
200 
201         let fns = self.device().fns();
202 
203         if self.device().api_version() >= Version::V1_3
204             || self.device().enabled_extensions().khr_copy_commands2
205         {
206             let regions: SmallVec<[_; 8]> = regions
207                 .iter()
208                 .map(|region| {
209                     let &BufferCopy {
210                         src_offset,
211                         dst_offset,
212                         size,
213                         _ne,
214                     } = region;
215 
216                     ash::vk::BufferCopy2 {
217                         src_offset: src_offset + src_buffer.offset(),
218                         dst_offset: dst_offset + dst_buffer.offset(),
219                         size,
220                         ..Default::default()
221                     }
222                 })
223                 .collect();
224 
225             let copy_buffer_info = ash::vk::CopyBufferInfo2 {
226                 src_buffer: src_buffer.buffer().handle(),
227                 dst_buffer: dst_buffer.buffer().handle(),
228                 region_count: regions.len() as u32,
229                 p_regions: regions.as_ptr(),
230                 ..Default::default()
231             };
232 
233             if self.device().api_version() >= Version::V1_3 {
234                 (fns.v1_3.cmd_copy_buffer2)(self.handle(), &copy_buffer_info);
235             } else {
236                 (fns.khr_copy_commands2.cmd_copy_buffer2_khr)(self.handle(), &copy_buffer_info);
237             }
238         } else {
239             let regions: SmallVec<[_; 8]> = regions
240                 .iter()
241                 .map(|region| {
242                     let &BufferCopy {
243                         src_offset,
244                         dst_offset,
245                         size,
246                         _ne,
247                     } = region;
248 
249                     ash::vk::BufferCopy {
250                         src_offset: src_offset + src_buffer.offset(),
251                         dst_offset: dst_offset + dst_buffer.offset(),
252                         size,
253                     }
254                 })
255                 .collect();
256 
257             (fns.v1_0.cmd_copy_buffer)(
258                 self.handle(),
259                 src_buffer.buffer().handle(),
260                 dst_buffer.buffer().handle(),
261                 regions.len() as u32,
262                 regions.as_ptr(),
263             );
264         }
265 
266         let command_index = self.next_command_index;
267         let command_name = "copy_buffer";
268         let src_use_ref = ResourceUseRef {
269             command_index,
270             command_name,
271             resource_in_command: ResourceInCommand::Source,
272             secondary_use_ref: None,
273         };
274         let dst_use_ref = ResourceUseRef {
275             command_index,
276             command_name,
277             resource_in_command: ResourceInCommand::Destination,
278             secondary_use_ref: None,
279         };
280 
281         for region in regions {
282             let BufferCopy {
283                 src_offset,
284                 dst_offset,
285                 size,
286                 _ne: _,
287             } = region;
288 
289             let mut src_range = src_offset..src_offset + size;
290             src_range.start += src_buffer.offset();
291             src_range.end += src_buffer.offset();
292             self.resources_usage_state.record_buffer_access(
293                 &src_use_ref,
294                 src_buffer.buffer(),
295                 src_range,
296                 PipelineStageAccess::Copy_TransferRead,
297             );
298 
299             let mut dst_range = dst_offset..dst_offset + size;
300             dst_range.start += dst_buffer.offset();
301             dst_range.end += dst_buffer.offset();
302             self.resources_usage_state.record_buffer_access(
303                 &dst_use_ref,
304                 dst_buffer.buffer(),
305                 dst_range,
306                 PipelineStageAccess::Copy_TransferWrite,
307             );
308         }
309 
310         self.resources.push(Box::new(src_buffer));
311         self.resources.push(Box::new(dst_buffer));
312 
313         self.next_command_index += 1;
314         self
315     }
316 
317     /// Copies data from an image to another image.
318     ///
319     /// There are several restrictions:
320     ///
321     /// - The number of samples in the source and destination images must be equal.
322     /// - The size of the uncompressed element format of the source image must be equal to the
323     ///   compressed element format of the destination.
324     /// - If you copy between depth, stencil or depth-stencil images, the format of both images
325     ///   must match exactly.
326     /// - For two-dimensional images, the Z coordinate must be 0 for the image offsets and 1 for
327     ///   the extent. Same for the Y coordinate for one-dimensional images.
328     /// - For non-array images, the base array layer must be 0 and the number of layers must be 1.
329     ///
330     /// If `layer_count` is greater than 1, the copy will happen between each individual layer as
331     /// if they were separate images.
332     ///
333     /// # Panics
334     ///
335     /// - Panics if `src_image` or `dst_image` were not created from the same device
336     ///   as `self`.
337     ///
338     /// # Safety
339     ///
340     /// - Appropriate synchronization must be provided for all images
341     ///   that are accessed by the command.
342     /// - All images that are accessed by the command must be in the expected image layout.
343     #[inline]
copy_image( &mut self, copy_image_info: CopyImageInfo, ) -> Result<&mut Self, CopyError>344     pub unsafe fn copy_image(
345         &mut self,
346         copy_image_info: CopyImageInfo,
347     ) -> Result<&mut Self, CopyError> {
348         self.validate_copy_image(&copy_image_info)?;
349 
350         unsafe { Ok(self.copy_image_unchecked(copy_image_info)) }
351     }
352 
validate_copy_image(&self, copy_image_info: &CopyImageInfo) -> Result<(), CopyError>353     fn validate_copy_image(&self, copy_image_info: &CopyImageInfo) -> Result<(), CopyError> {
354         let device = self.device();
355 
356         // VUID-vkCmdCopyImage2-renderpass
357         if self.builder_state.render_pass.is_some() {
358             return Err(CopyError::ForbiddenInsideRenderPass);
359         }
360 
361         let queue_family_properties = self.queue_family_properties();
362 
363         // VUID-vkCmdCopyImage2-commandBuffer-cmdpool
364         if !queue_family_properties
365             .queue_flags
366             .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
367         {
368             return Err(CopyError::NotSupportedByQueueFamily);
369         }
370 
371         let &CopyImageInfo {
372             ref src_image,
373             src_image_layout,
374             ref dst_image,
375             dst_image_layout,
376             ref regions,
377             _ne: _,
378         } = copy_image_info;
379 
380         // VUID-VkCopyImageInfo2-srcImageLayout-parameter
381         src_image_layout.validate_device(device)?;
382 
383         // VUID-VkCopyImageInfo2-dstImageLayout-parameter
384         dst_image_layout.validate_device(device)?;
385 
386         // VUID-VkCopyImageInfo2-commonparent
387         assert_eq!(device, src_image.device());
388         assert_eq!(device, dst_image.device());
389 
390         let copy_2d_3d_supported =
391             device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1;
392         let src_image_inner = src_image.inner();
393         let dst_image_inner = dst_image.inner();
394         let mut src_image_aspects = src_image.format().aspects();
395         let mut dst_image_aspects = dst_image.format().aspects();
396 
397         if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
398             // VUID-VkCopyImageInfo2-srcImage-01995
399             if !src_image
400                 .format_features()
401                 .intersects(FormatFeatures::TRANSFER_SRC)
402             {
403                 return Err(CopyError::MissingFormatFeature {
404                     resource: CopyErrorResource::Source,
405                     format_feature: "transfer_src",
406                 });
407             }
408 
409             // VUID-VkCopyImageInfo2-dstImage-01996
410             if !dst_image
411                 .format_features()
412                 .intersects(FormatFeatures::TRANSFER_DST)
413             {
414                 return Err(CopyError::MissingFormatFeature {
415                     resource: CopyErrorResource::Destination,
416                     format_feature: "transfer_dst",
417                 });
418             }
419         }
420 
421         // VUID-VkCopyImageInfo2-srcImage-00136
422         if src_image.samples() != dst_image.samples() {
423             return Err(CopyError::SampleCountMismatch {
424                 src_sample_count: src_image.samples(),
425                 dst_sample_count: dst_image.samples(),
426             });
427         }
428 
429         if !(src_image_aspects.intersects(ImageAspects::COLOR)
430             || dst_image_aspects.intersects(ImageAspects::COLOR))
431         {
432             // VUID-VkCopyImageInfo2-srcImage-01548
433             if src_image.format() != dst_image.format() {
434                 return Err(CopyError::FormatsMismatch {
435                     src_format: src_image.format(),
436                     dst_format: dst_image.format(),
437                 });
438             }
439         }
440 
441         // VUID-VkCopyImageInfo2-srcImageLayout-01917
442         if !matches!(
443             src_image_layout,
444             ImageLayout::TransferSrcOptimal | ImageLayout::General
445         ) {
446             return Err(CopyError::ImageLayoutInvalid {
447                 resource: CopyErrorResource::Source,
448                 image_layout: src_image_layout,
449             });
450         }
451 
452         // VUID-VkCopyImageInfo2-dstImageLayout-01395
453         if !matches!(
454             dst_image_layout,
455             ImageLayout::TransferDstOptimal | ImageLayout::General
456         ) {
457             return Err(CopyError::ImageLayoutInvalid {
458                 resource: CopyErrorResource::Destination,
459                 image_layout: dst_image_layout,
460             });
461         }
462 
463         let extent_alignment = match queue_family_properties.min_image_transfer_granularity {
464             [0, 0, 0] => None,
465             min_image_transfer_granularity => {
466                 let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| {
467                     if is_multi_plane {
468                         // Assume planes always have 1x1 blocks
469                         min_image_transfer_granularity
470                     } else {
471                         // "The value returned in minImageTransferGranularity has a unit of
472                         // compressed texel blocks for images having a block-compressed format, and
473                         // a unit of texels otherwise."
474                         [
475                             min_image_transfer_granularity[0] * block_extent[0],
476                             min_image_transfer_granularity[1] * block_extent[1],
477                             min_image_transfer_granularity[2] * block_extent[2],
478                         ]
479                     }
480                 };
481 
482                 Some((
483                     granularity(
484                         src_image.format().block_extent(),
485                         src_image_aspects.intersects(ImageAspects::PLANE_0),
486                     ),
487                     granularity(
488                         dst_image.format().block_extent(),
489                         dst_image_aspects.intersects(ImageAspects::PLANE_0),
490                     ),
491                 ))
492             }
493         };
494 
495         if src_image_aspects.intersects(ImageAspects::PLANE_0) {
496             // VUID-VkCopyImageInfo2-srcImage-01552
497             // VUID-VkCopyImageInfo2-srcImage-01553
498             src_image_aspects -= ImageAspects::COLOR;
499         }
500 
501         if dst_image_aspects.intersects(ImageAspects::PLANE_0) {
502             // VUID-VkCopyImageInfo2-dstImage-01554
503             // VUID-VkCopyImageInfo2-dstImage-01555
504             dst_image_aspects -= ImageAspects::COLOR;
505         }
506 
507         let mut src_image_aspects_used = ImageAspects::empty();
508         let mut dst_image_aspects_used = ImageAspects::empty();
509         let same_image = src_image_inner.image == dst_image_inner.image;
510         let mut overlap_subresource_indices = None;
511         let mut overlap_extent_indices = None;
512 
513         for (region_index, region) in regions.iter().enumerate() {
514             let &ImageCopy {
515                 ref src_subresource,
516                 src_offset,
517                 ref dst_subresource,
518                 dst_offset,
519                 extent,
520                 _ne,
521             } = region;
522 
523             let check_subresource = |resource: CopyErrorResource,
524                                      image: &dyn ImageAccess,
525                                      image_aspects: ImageAspects,
526                                      subresource: &ImageSubresourceLayers|
527              -> Result<_, CopyError> {
528                 // VUID-VkCopyImageInfo2-srcSubresource-01696
529                 // VUID-VkCopyImageInfo2-dstSubresource-01697
530                 if subresource.mip_level >= image.mip_levels() {
531                     return Err(CopyError::MipLevelsOutOfRange {
532                         resource,
533                         region_index,
534                         mip_levels_range_end: subresource.mip_level + 1,
535                         image_mip_levels: image.mip_levels(),
536                     });
537                 }
538 
539                 // VUID-VkImageSubresourceLayers-layerCount-01700
540                 assert!(!subresource.array_layers.is_empty());
541 
542                 // VUID-VkCopyImageInfo2-srcSubresource-01698
543                 // VUID-VkCopyImageInfo2-dstSubresource-01699
544                 // VUID-VkCopyImageInfo2-srcImage-04443
545                 // VUID-VkCopyImageInfo2-dstImage-04444
546                 if subresource.array_layers.end > image.dimensions().array_layers() {
547                     return Err(CopyError::ArrayLayersOutOfRange {
548                         resource,
549                         region_index,
550                         array_layers_range_end: subresource.array_layers.end,
551                         image_array_layers: image.dimensions().array_layers(),
552                     });
553                 }
554 
555                 // VUID-VkImageSubresourceLayers-aspectMask-parameter
556                 subresource.aspects.validate_device(device)?;
557 
558                 // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask
559                 assert!(!subresource.aspects.is_empty());
560 
561                 // VUID-VkCopyImageInfo2-aspectMask-00142
562                 // VUID-VkCopyImageInfo2-aspectMask-00143
563                 if !image_aspects.contains(subresource.aspects) {
564                     return Err(CopyError::AspectsNotAllowed {
565                         resource,
566                         region_index,
567                         aspects: subresource.aspects,
568                         allowed_aspects: image_aspects,
569                     });
570                 }
571 
572                 let (subresource_format, subresource_extent) =
573                     if image_aspects.intersects(ImageAspects::PLANE_0) {
574                         // VUID-VkCopyImageInfo2-srcImage-01552
575                         // VUID-VkCopyImageInfo2-srcImage-01553
576                         // VUID-VkCopyImageInfo2-dstImage-01554
577                         // VUID-VkCopyImageInfo2-dstImage-01555
578                         if subresource.aspects.count() != 1 {
579                             return Err(CopyError::MultipleAspectsNotAllowed {
580                                 resource,
581                                 region_index,
582                                 aspects: subresource.aspects,
583                             });
584                         }
585 
586                         if subresource.aspects.intersects(ImageAspects::PLANE_0) {
587                             (
588                                 image.format().planes()[0],
589                                 image.dimensions().width_height_depth(),
590                             )
591                         } else if subresource.aspects.intersects(ImageAspects::PLANE_1) {
592                             (
593                                 image.format().planes()[1],
594                                 image
595                                     .format()
596                                     .ycbcr_chroma_sampling()
597                                     .unwrap()
598                                     .subsampled_extent(image.dimensions().width_height_depth()),
599                             )
600                         } else {
601                             (
602                                 image.format().planes()[2],
603                                 image
604                                     .format()
605                                     .ycbcr_chroma_sampling()
606                                     .unwrap()
607                                     .subsampled_extent(image.dimensions().width_height_depth()),
608                             )
609                         }
610                     } else {
611                         (
612                             image.format(),
613                             image
614                                 .dimensions()
615                                 .mip_level_dimensions(subresource.mip_level)
616                                 .unwrap()
617                                 .width_height_depth(),
618                         )
619                     };
620 
621                 Ok((subresource_format, subresource_extent))
622             };
623 
624             src_image_aspects_used |= src_subresource.aspects;
625             dst_image_aspects_used |= dst_subresource.aspects;
626 
627             let (src_subresource_format, src_subresource_extent) = check_subresource(
628                 CopyErrorResource::Source,
629                 src_image,
630                 src_image_aspects,
631                 src_subresource,
632             )?;
633             let (dst_subresource_format, dst_subresource_extent) = check_subresource(
634                 CopyErrorResource::Destination,
635                 dst_image,
636                 dst_image_aspects,
637                 dst_subresource,
638             )?;
639 
640             if !(src_image_aspects.intersects(ImageAspects::PLANE_0)
641                 || dst_image_aspects.intersects(ImageAspects::PLANE_0))
642             {
643                 // VUID-VkCopyImageInfo2-srcImage-01551
644                 if src_subresource.aspects != dst_subresource.aspects {
645                     return Err(CopyError::AspectsMismatch {
646                         region_index,
647                         src_aspects: src_subresource.aspects,
648                         dst_aspects: dst_subresource.aspects,
649                     });
650                 }
651             }
652 
653             // VUID-VkCopyImageInfo2-srcImage-01548
654             // VUID-VkCopyImageInfo2-None-01549
655             // Color formats must be size-compatible.
656             if src_subresource_format.block_size() != dst_subresource_format.block_size() {
657                 return Err(CopyError::FormatsNotCompatible {
658                     src_format: src_subresource_format,
659                     dst_format: dst_subresource_format,
660                 });
661             }
662 
663             // TODO:
664             // "When copying between compressed and uncompressed formats the extent members
665             // represent the texel dimensions of the source image and not the destination."
666             let mut src_extent = extent;
667             let mut dst_extent = extent;
668             let src_layer_count =
669                 src_subresource.array_layers.end - src_subresource.array_layers.start;
670             let dst_layer_count =
671                 dst_subresource.array_layers.end - dst_subresource.array_layers.start;
672 
673             if copy_2d_3d_supported {
674                 match (
675                     src_image.dimensions().image_type(),
676                     dst_image.dimensions().image_type(),
677                 ) {
678                     (ImageType::Dim2d, ImageType::Dim3d) => {
679                         src_extent[2] = 1;
680 
681                         // VUID-vkCmdCopyImage-srcImage-01791
682                         if dst_extent[2] != src_layer_count {
683                             return Err(CopyError::ArrayLayerCountMismatch {
684                                 region_index,
685                                 src_layer_count,
686                                 dst_layer_count: dst_extent[2],
687                             });
688                         }
689                     }
690                     (ImageType::Dim3d, ImageType::Dim2d) => {
691                         dst_extent[2] = 1;
692 
693                         // VUID-vkCmdCopyImage-dstImage-01792
694                         if src_extent[2] != dst_layer_count {
695                             return Err(CopyError::ArrayLayerCountMismatch {
696                                 region_index,
697                                 src_layer_count: src_extent[2],
698                                 dst_layer_count,
699                             });
700                         }
701                     }
702                     _ => {
703                         // VUID-VkImageCopy2-extent-00140
704                         if src_layer_count != dst_layer_count {
705                             return Err(CopyError::ArrayLayerCountMismatch {
706                                 region_index,
707                                 src_layer_count,
708                                 dst_layer_count,
709                             });
710                         }
711                     }
712                 }
713             } else {
714                 // VUID-VkImageCopy2-extent-00140
715                 if src_layer_count != dst_layer_count {
716                     return Err(CopyError::ArrayLayerCountMismatch {
717                         region_index,
718                         src_layer_count,
719                         dst_layer_count,
720                     });
721                 }
722             };
723 
724             if let Some((src_extent_alignment, dst_extent_alignment)) = extent_alignment {
725                 let check_offset_extent = |resource: CopyErrorResource,
726                                            extent_alignment: [u32; 3],
727                                            subresource_extent: [u32; 3],
728                                            offset: [u32; 3],
729                                            extent: [u32; 3]|
730                  -> Result<_, CopyError> {
731                     for i in 0..3 {
732                         // VUID-VkImageCopy2-extent-06668
733                         // VUID-VkImageCopy2-extent-06669
734                         // VUID-VkImageCopy2-extent-06670
735                         assert!(extent[i] != 0);
736 
737                         // VUID-VkCopyImageInfo2-srcOffset-00144
738                         // VUID-VkCopyImageInfo2-srcOffset-00145
739                         // VUID-VkCopyImageInfo2-srcOffset-00147
740                         // VUID-VkCopyImageInfo2-dstOffset-00150
741                         // VUID-VkCopyImageInfo2-dstOffset-00151
742                         // VUID-VkCopyImageInfo2-dstOffset-00153
743                         if offset[i] + extent[i] > subresource_extent[i] {
744                             return Err(CopyError::RegionOutOfImageBounds {
745                                 resource,
746                                 region_index,
747                                 offset_range_end: [
748                                     offset[0] + extent[0],
749                                     offset[1] + extent[1],
750                                     offset[2] + extent[2],
751                                 ],
752                                 subresource_extent,
753                             });
754                         }
755 
756                         // VUID-VkCopyImageInfo2-srcImage-01727
757                         // VUID-VkCopyImageInfo2-dstImage-01731
758                         // VUID-VkCopyImageInfo2-srcOffset-01783
759                         // VUID-VkCopyImageInfo2-dstOffset-01784
760                         if offset[i] % extent_alignment[i] != 0 {
761                             return Err(CopyError::OffsetNotAlignedForImage {
762                                 resource,
763                                 region_index,
764                                 offset,
765                                 required_alignment: extent_alignment,
766                             });
767                         }
768 
769                         // VUID-VkCopyImageInfo2-srcImage-01728
770                         // VUID-VkCopyImageInfo2-srcImage-01729
771                         // VUID-VkCopyImageInfo2-srcImage-01730
772                         // VUID-VkCopyImageInfo2-dstImage-01732
773                         // VUID-VkCopyImageInfo2-dstImage-01733
774                         // VUID-VkCopyImageInfo2-dstImage-01734
775                         if offset[i] + extent[i] != subresource_extent[i]
776                             && extent[i] % extent_alignment[i] != 0
777                         {
778                             return Err(CopyError::ExtentNotAlignedForImage {
779                                 resource,
780                                 region_index,
781                                 extent,
782                                 required_alignment: extent_alignment,
783                             });
784                         }
785                     }
786 
787                     Ok(())
788                 };
789 
790                 check_offset_extent(
791                     CopyErrorResource::Source,
792                     src_extent_alignment,
793                     src_subresource_extent,
794                     src_offset,
795                     src_extent,
796                 )?;
797                 check_offset_extent(
798                     CopyErrorResource::Destination,
799                     dst_extent_alignment,
800                     dst_subresource_extent,
801                     dst_offset,
802                     dst_extent,
803                 )?;
804 
805                 // VUID-VkCopyImageInfo2-pRegions-00124
806                 if same_image {
807                     let src_region_index = region_index;
808                     let src_subresource_axes = [
809                         src_image_inner.first_mipmap_level + src_subresource.mip_level
810                             ..src_image_inner.first_mipmap_level + src_subresource.mip_level + 1,
811                         src_image_inner.first_layer + src_subresource.array_layers.start
812                             ..src_image_inner.first_layer + src_subresource.array_layers.end,
813                     ];
814                     let src_extent_axes = [
815                         src_offset[0]..src_offset[0] + extent[0],
816                         src_offset[1]..src_offset[1] + extent[1],
817                         src_offset[2]..src_offset[2] + extent[2],
818                     ];
819 
820                     for (dst_region_index, dst_region) in regions.iter().enumerate() {
821                         let &ImageCopy {
822                             ref dst_subresource,
823                             dst_offset,
824                             ..
825                         } = dst_region;
826 
827                         // For a single-plane image, the aspects must always be identical anyway
828                         if src_image_aspects.intersects(ImageAspects::PLANE_0)
829                             && src_subresource.aspects != dst_subresource.aspects
830                         {
831                             continue;
832                         }
833 
834                         let dst_subresource_axes = [
835                             dst_image_inner.first_mipmap_level + dst_subresource.mip_level
836                                 ..dst_image_inner.first_mipmap_level
837                                     + dst_subresource.mip_level
838                                     + 1,
839                             dst_image_inner.first_layer + src_subresource.array_layers.start
840                                 ..dst_image_inner.first_layer + src_subresource.array_layers.end,
841                         ];
842 
843                         if src_subresource_axes.iter().zip(dst_subresource_axes).any(
844                             |(src_range, dst_range)| {
845                                 src_range.start >= dst_range.end || dst_range.start >= src_range.end
846                             },
847                         ) {
848                             continue;
849                         }
850 
851                         // If the subresource axes all overlap, then the source and destination must
852                         // have the same layout.
853                         overlap_subresource_indices = Some((src_region_index, dst_region_index));
854 
855                         let dst_extent_axes = [
856                             dst_offset[0]..dst_offset[0] + extent[0],
857                             dst_offset[1]..dst_offset[1] + extent[1],
858                             dst_offset[2]..dst_offset[2] + extent[2],
859                         ];
860 
861                         // There is only overlap if all of the axes overlap.
862                         if src_extent_axes.iter().zip(dst_extent_axes).any(
863                             |(src_range, dst_range)| {
864                                 src_range.start >= dst_range.end || dst_range.start >= src_range.end
865                             },
866                         ) {
867                             continue;
868                         }
869 
870                         overlap_extent_indices = Some((src_region_index, dst_region_index));
871                     }
872                 }
873             } else {
874                 // If granularity is `None`, then we can only copy whole subresources.
875                 let check_offset_extent = |resource: CopyErrorResource,
876                                            subresource_extent: [u32; 3],
877                                            offset: [u32; 3],
878                                            extent: [u32; 3]|
879                  -> Result<_, CopyError> {
880                     // VUID-VkCopyImageInfo2-srcImage-01727
881                     // VUID-VkCopyImageInfo2-dstImage-01731
882                     // VUID-vkCmdCopyImage-srcOffset-01783
883                     // VUID-vkCmdCopyImage-dstOffset-01784
884                     if offset != [0, 0, 0] {
885                         return Err(CopyError::OffsetNotAlignedForImage {
886                             resource,
887                             region_index,
888                             offset,
889                             required_alignment: subresource_extent,
890                         });
891                     }
892 
893                     // VUID-VkCopyImageInfo2-srcImage-01728
894                     // VUID-VkCopyImageInfo2-srcImage-01729
895                     // VUID-VkCopyImageInfo2-srcImage-01730
896                     // VUID-VkCopyImageInfo2-dstImage-01732
897                     // VUID-VkCopyImageInfo2-dstImage-01733
898                     // VUID-VkCopyImageInfo2-dstImage-01734
899                     if extent != subresource_extent {
900                         return Err(CopyError::ExtentNotAlignedForImage {
901                             resource,
902                             region_index,
903                             extent,
904                             required_alignment: subresource_extent,
905                         });
906                     }
907 
908                     Ok(())
909                 };
910 
911                 check_offset_extent(
912                     CopyErrorResource::Source,
913                     src_subresource_extent,
914                     src_offset,
915                     src_extent,
916                 )?;
917                 check_offset_extent(
918                     CopyErrorResource::Destination,
919                     dst_subresource_extent,
920                     dst_offset,
921                     dst_extent,
922                 )?;
923 
924                 // VUID-VkCopyImageInfo2-pRegions-00124
925                 // A simpler version that assumes the region covers the full extent.
926                 if same_image {
927                     let src_region_index = region_index;
928                     let src_axes = [
929                         src_image_inner.first_mipmap_level + src_subresource.mip_level
930                             ..src_image_inner.first_mipmap_level + src_subresource.mip_level + 1,
931                         src_image_inner.first_layer + src_subresource.array_layers.start
932                             ..src_image_inner.first_layer + src_subresource.array_layers.end,
933                     ];
934 
935                     for (dst_region_index, dst_region) in regions.iter().enumerate() {
936                         let &ImageCopy {
937                             ref dst_subresource,
938                             dst_offset: _,
939                             ..
940                         } = dst_region;
941 
942                         if src_image_aspects.intersects(ImageAspects::PLANE_0)
943                             && src_subresource.aspects != dst_subresource.aspects
944                         {
945                             continue;
946                         }
947 
948                         let dst_axes = [
949                             dst_image_inner.first_mipmap_level + dst_subresource.mip_level
950                                 ..dst_image_inner.first_mipmap_level
951                                     + dst_subresource.mip_level
952                                     + 1,
953                             dst_image_inner.first_layer + src_subresource.array_layers.start
954                                 ..dst_image_inner.first_layer + src_subresource.array_layers.end,
955                         ];
956 
957                         // There is only overlap if all of the axes overlap.
958                         if src_axes.iter().zip(dst_axes).any(|(src_range, dst_range)| {
959                             src_range.start >= dst_range.end || dst_range.start >= src_range.end
960                         }) {
961                             continue;
962                         }
963 
964                         overlap_extent_indices = Some((src_region_index, dst_region_index));
965                     }
966                 }
967             }
968         }
969 
970         // VUID-VkCopyImageInfo2-aspect-06662
971         if !(src_image_aspects_used - ImageAspects::STENCIL).is_empty()
972             && !src_image.usage().intersects(ImageUsage::TRANSFER_SRC)
973         {
974             return Err(CopyError::MissingUsage {
975                 resource: CopyErrorResource::Source,
976                 usage: "transfer_src",
977             });
978         }
979 
980         // VUID-VkCopyImageInfo2-aspect-06663
981         if !(dst_image_aspects_used - ImageAspects::STENCIL).is_empty()
982             && !dst_image.usage().intersects(ImageUsage::TRANSFER_DST)
983         {
984             return Err(CopyError::MissingUsage {
985                 resource: CopyErrorResource::Destination,
986                 usage: "transfer_dst",
987             });
988         }
989 
990         // VUID-VkCopyImageInfo2-aspect-06664
991         if src_image_aspects_used.intersects(ImageAspects::STENCIL)
992             && !src_image
993                 .stencil_usage()
994                 .intersects(ImageUsage::TRANSFER_SRC)
995         {
996             return Err(CopyError::MissingUsage {
997                 resource: CopyErrorResource::Source,
998                 usage: "transfer_src",
999             });
1000         }
1001 
1002         // VUID-VkCopyImageInfo2-aspect-06665
1003         if dst_image_aspects_used.intersects(ImageAspects::STENCIL)
1004             && !dst_image
1005                 .stencil_usage()
1006                 .intersects(ImageUsage::TRANSFER_DST)
1007         {
1008             return Err(CopyError::MissingUsage {
1009                 resource: CopyErrorResource::Destination,
1010                 usage: "transfer_dst",
1011             });
1012         }
1013 
1014         // VUID-VkCopyImageInfo2-pRegions-00124
1015         if let Some((src_region_index, dst_region_index)) = overlap_extent_indices {
1016             return Err(CopyError::OverlappingRegions {
1017                 src_region_index,
1018                 dst_region_index,
1019             });
1020         }
1021 
1022         // VUID-VkCopyImageInfo2-srcImageLayout-00128
1023         // VUID-VkCopyImageInfo2-dstImageLayout-00133
1024         if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices {
1025             if src_image_layout != dst_image_layout {
1026                 return Err(CopyError::OverlappingSubresourcesLayoutMismatch {
1027                     src_region_index,
1028                     dst_region_index,
1029                     src_image_layout,
1030                     dst_image_layout,
1031                 });
1032             }
1033         }
1034 
1035         // TODO: sync check
1036 
1037         Ok(())
1038     }
1039 
1040     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
copy_image_unchecked(&mut self, copy_image_info: CopyImageInfo) -> &mut Self1041     pub unsafe fn copy_image_unchecked(&mut self, copy_image_info: CopyImageInfo) -> &mut Self {
1042         let CopyImageInfo {
1043             src_image,
1044             src_image_layout,
1045             dst_image,
1046             dst_image_layout,
1047             regions,
1048             _ne: _,
1049         } = copy_image_info;
1050 
1051         if regions.is_empty() {
1052             return self;
1053         }
1054 
1055         let src_image_inner = src_image.inner();
1056         let dst_image_inner = dst_image.inner();
1057 
1058         let fns = self.device().fns();
1059 
1060         if self.device().api_version() >= Version::V1_3
1061             || self.device().enabled_extensions().khr_copy_commands2
1062         {
1063             let regions: SmallVec<[_; 8]> = regions
1064                 .iter()
1065                 .map(|region| {
1066                     let &ImageCopy {
1067                         ref src_subresource,
1068                         src_offset,
1069                         ref dst_subresource,
1070                         dst_offset,
1071                         extent,
1072                         _ne: _,
1073                     } = region;
1074 
1075                     let mut src_subresource = src_subresource.clone();
1076                     src_subresource.array_layers.start += src_image_inner.first_layer;
1077                     src_subresource.array_layers.end += src_image_inner.first_layer;
1078                     src_subresource.mip_level += src_image_inner.first_mipmap_level;
1079 
1080                     let mut dst_subresource = dst_subresource.clone();
1081                     dst_subresource.array_layers.start += dst_image_inner.first_layer;
1082                     dst_subresource.array_layers.end += dst_image_inner.first_layer;
1083                     dst_subresource.mip_level += dst_image_inner.first_mipmap_level;
1084 
1085                     ash::vk::ImageCopy2 {
1086                         src_subresource: src_subresource.into(),
1087                         src_offset: ash::vk::Offset3D {
1088                             x: src_offset[0] as i32,
1089                             y: src_offset[1] as i32,
1090                             z: src_offset[2] as i32,
1091                         },
1092                         dst_subresource: dst_subresource.into(),
1093                         dst_offset: ash::vk::Offset3D {
1094                             x: dst_offset[0] as i32,
1095                             y: dst_offset[1] as i32,
1096                             z: dst_offset[2] as i32,
1097                         },
1098                         extent: ash::vk::Extent3D {
1099                             width: extent[0],
1100                             height: extent[1],
1101                             depth: extent[2],
1102                         },
1103                         ..Default::default()
1104                     }
1105                 })
1106                 .collect();
1107 
1108             let copy_image_info = ash::vk::CopyImageInfo2 {
1109                 src_image: src_image_inner.image.handle(),
1110                 src_image_layout: src_image_layout.into(),
1111                 dst_image: dst_image_inner.image.handle(),
1112                 dst_image_layout: dst_image_layout.into(),
1113                 region_count: regions.len() as u32,
1114                 p_regions: regions.as_ptr(),
1115                 ..Default::default()
1116             };
1117 
1118             if self.device().api_version() >= Version::V1_3 {
1119                 (fns.v1_3.cmd_copy_image2)(self.handle(), &copy_image_info);
1120             } else {
1121                 (fns.khr_copy_commands2.cmd_copy_image2_khr)(self.handle(), &copy_image_info);
1122             }
1123         } else {
1124             let regions: SmallVec<[_; 8]> = regions
1125                 .iter()
1126                 .map(|region| {
1127                     let &ImageCopy {
1128                         ref src_subresource,
1129                         src_offset,
1130                         ref dst_subresource,
1131                         dst_offset,
1132                         extent,
1133                         _ne: _,
1134                     } = region;
1135 
1136                     let mut src_subresource = src_subresource.clone();
1137                     src_subresource.array_layers.start += src_image_inner.first_layer;
1138                     src_subresource.array_layers.end += src_image_inner.first_layer;
1139                     src_subresource.mip_level += src_image_inner.first_mipmap_level;
1140 
1141                     let mut dst_subresource = dst_subresource.clone();
1142                     dst_subresource.array_layers.start += dst_image_inner.first_layer;
1143                     dst_subresource.array_layers.end += dst_image_inner.first_layer;
1144                     dst_subresource.mip_level += dst_image_inner.first_mipmap_level;
1145 
1146                     ash::vk::ImageCopy {
1147                         src_subresource: src_subresource.into(),
1148                         src_offset: ash::vk::Offset3D {
1149                             x: src_offset[0] as i32,
1150                             y: src_offset[1] as i32,
1151                             z: src_offset[2] as i32,
1152                         },
1153                         dst_subresource: dst_subresource.into(),
1154                         dst_offset: ash::vk::Offset3D {
1155                             x: dst_offset[0] as i32,
1156                             y: dst_offset[1] as i32,
1157                             z: dst_offset[2] as i32,
1158                         },
1159                         extent: ash::vk::Extent3D {
1160                             width: extent[0],
1161                             height: extent[1],
1162                             depth: extent[2],
1163                         },
1164                     }
1165                 })
1166                 .collect();
1167 
1168             (fns.v1_0.cmd_copy_image)(
1169                 self.handle(),
1170                 src_image_inner.image.handle(),
1171                 src_image_layout.into(),
1172                 dst_image_inner.image.handle(),
1173                 dst_image_layout.into(),
1174                 regions.len() as u32,
1175                 regions.as_ptr(),
1176             );
1177         }
1178 
1179         let command_index = self.next_command_index;
1180         let command_name = "copy_image";
1181         let src_use_ref = ResourceUseRef {
1182             command_index,
1183             command_name,
1184             resource_in_command: ResourceInCommand::Source,
1185             secondary_use_ref: None,
1186         };
1187         let dst_use_ref = ResourceUseRef {
1188             command_index,
1189             command_name,
1190             resource_in_command: ResourceInCommand::Destination,
1191             secondary_use_ref: None,
1192         };
1193 
1194         for region in regions {
1195             let ImageCopy {
1196                 src_subresource,
1197                 src_offset: _,
1198                 dst_subresource,
1199                 dst_offset: _,
1200                 extent: _,
1201                 _ne: _,
1202             } = region;
1203 
1204             let mut src_subresource_range = ImageSubresourceRange::from(src_subresource);
1205             src_subresource_range.array_layers.start += src_image_inner.first_layer;
1206             src_subresource_range.array_layers.end += src_image_inner.first_layer;
1207             src_subresource_range.mip_levels.start += src_image_inner.first_mipmap_level;
1208             src_subresource_range.mip_levels.end += src_image_inner.first_mipmap_level;
1209             self.resources_usage_state.record_image_access(
1210                 &src_use_ref,
1211                 src_image_inner.image,
1212                 src_subresource_range,
1213                 PipelineStageAccess::Copy_TransferRead,
1214                 src_image_layout,
1215             );
1216 
1217             let mut dst_subresource_range = ImageSubresourceRange::from(dst_subresource);
1218             dst_subresource_range.array_layers.start += dst_image_inner.first_layer;
1219             dst_subresource_range.array_layers.end += dst_image_inner.first_layer;
1220             dst_subresource_range.mip_levels.start += dst_image_inner.first_mipmap_level;
1221             dst_subresource_range.mip_levels.end += dst_image_inner.first_mipmap_level;
1222             self.resources_usage_state.record_image_access(
1223                 &dst_use_ref,
1224                 dst_image_inner.image,
1225                 dst_subresource_range,
1226                 PipelineStageAccess::Copy_TransferWrite,
1227                 dst_image_layout,
1228             );
1229         }
1230 
1231         self.resources.push(Box::new(src_image));
1232         self.resources.push(Box::new(dst_image));
1233 
1234         self.next_command_index += 1;
1235         self
1236     }
1237 
1238     /// Copies from a buffer to an image.
1239     ///
1240     /// # Safety
1241     ///
1242     /// - Appropriate synchronization must be provided for all buffers and images
1243     ///   that are accessed by the command.
1244     /// - All images that are accessed by the command must be in the expected image layout.
1245     #[inline]
copy_buffer_to_image( &mut self, copy_buffer_to_image_info: CopyBufferToImageInfo, ) -> Result<&mut Self, CopyError>1246     pub unsafe fn copy_buffer_to_image(
1247         &mut self,
1248         copy_buffer_to_image_info: CopyBufferToImageInfo,
1249     ) -> Result<&mut Self, CopyError> {
1250         self.validate_copy_buffer_to_image(&copy_buffer_to_image_info)?;
1251 
1252         unsafe { Ok(self.copy_buffer_to_image_unchecked(copy_buffer_to_image_info)) }
1253     }
1254 
validate_copy_buffer_to_image( &self, copy_buffer_to_image_info: &CopyBufferToImageInfo, ) -> Result<(), CopyError>1255     fn validate_copy_buffer_to_image(
1256         &self,
1257         copy_buffer_to_image_info: &CopyBufferToImageInfo,
1258     ) -> Result<(), CopyError> {
1259         let device = self.device();
1260 
1261         // VUID-vkCmdCopyBufferToImage2-renderpass
1262         if self.builder_state.render_pass.is_some() {
1263             return Err(CopyError::ForbiddenInsideRenderPass);
1264         }
1265 
1266         let queue_family_properties = self.queue_family_properties();
1267 
1268         // VUID-vkCmdCopyBufferToImage2-commandBuffer-cmdpool
1269         if !queue_family_properties
1270             .queue_flags
1271             .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
1272         {
1273             return Err(CopyError::NotSupportedByQueueFamily);
1274         }
1275 
1276         let &CopyBufferToImageInfo {
1277             ref src_buffer,
1278             ref dst_image,
1279             dst_image_layout,
1280             ref regions,
1281             _ne: _,
1282         } = copy_buffer_to_image_info;
1283 
1284         // VUID-VkCopyBufferToImageInfo2-dstImageLayout-parameter
1285         dst_image_layout.validate_device(device)?;
1286 
1287         // VUID-VkCopyBufferToImageInfo2-commonparent
1288         assert_eq!(device, src_buffer.device());
1289         assert_eq!(device, dst_image.device());
1290 
1291         let mut image_aspects = dst_image.format().aspects();
1292 
1293         // VUID-VkCopyBufferToImageInfo2-commandBuffer-04477
1294         if !queue_family_properties
1295             .queue_flags
1296             .intersects(QueueFlags::GRAPHICS)
1297             && !image_aspects.intersects(ImageAspects::COLOR)
1298         {
1299             return Err(CopyError::DepthStencilNotSupportedByQueueFamily);
1300         }
1301 
1302         // VUID-VkCopyBufferToImageInfo2-srcBuffer-00174
1303         if !src_buffer
1304             .buffer()
1305             .usage()
1306             .intersects(BufferUsage::TRANSFER_SRC)
1307         {
1308             return Err(CopyError::MissingUsage {
1309                 resource: CopyErrorResource::Source,
1310                 usage: "transfer_src",
1311             });
1312         }
1313 
1314         // VUID-VkCopyBufferToImageInfo2-dstImage-00177
1315         if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) {
1316             return Err(CopyError::MissingUsage {
1317                 resource: CopyErrorResource::Destination,
1318                 usage: "transfer_dst",
1319             });
1320         }
1321 
1322         if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
1323             // VUID-VkCopyBufferToImageInfo2-dstImage-01997
1324             if !dst_image
1325                 .format_features()
1326                 .intersects(FormatFeatures::TRANSFER_DST)
1327             {
1328                 return Err(CopyError::MissingFormatFeature {
1329                     resource: CopyErrorResource::Destination,
1330                     format_feature: "transfer_dst",
1331                 });
1332             }
1333         }
1334 
1335         // VUID-VkCopyBufferToImageInfo2-dstImage-00179
1336         if dst_image.samples() != SampleCount::Sample1 {
1337             return Err(CopyError::SampleCountInvalid {
1338                 resource: CopyErrorResource::Destination,
1339                 sample_count: dst_image.samples(),
1340                 allowed_sample_counts: SampleCounts::SAMPLE_1,
1341             });
1342         }
1343 
1344         // VUID-VkCopyBufferToImageInfo2-dstImageLayout-01396
1345         if !matches!(
1346             dst_image_layout,
1347             ImageLayout::TransferDstOptimal | ImageLayout::General
1348         ) {
1349             return Err(CopyError::ImageLayoutInvalid {
1350                 resource: CopyErrorResource::Destination,
1351                 image_layout: dst_image_layout,
1352             });
1353         }
1354 
1355         let extent_alignment = match queue_family_properties.min_image_transfer_granularity {
1356             [0, 0, 0] => None,
1357             min_image_transfer_granularity => {
1358                 let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| {
1359                     if is_multi_plane {
1360                         // Assume planes always have 1x1 blocks
1361                         min_image_transfer_granularity
1362                     } else {
1363                         // "The value returned in minImageTransferGranularity has a unit of
1364                         // compressed texel blocks for images having a block-compressed format, and
1365                         // a unit of texels otherwise."
1366                         [
1367                             min_image_transfer_granularity[0] * block_extent[0],
1368                             min_image_transfer_granularity[1] * block_extent[1],
1369                             min_image_transfer_granularity[2] * block_extent[2],
1370                         ]
1371                     }
1372                 };
1373 
1374                 Some(granularity(
1375                     dst_image.format().block_extent(),
1376                     image_aspects.intersects(ImageAspects::PLANE_0),
1377                 ))
1378             }
1379         };
1380 
1381         if image_aspects.intersects(ImageAspects::PLANE_0) {
1382             // VUID-VkCopyBufferToImageInfo2-aspectMask-01560
1383             image_aspects -= ImageAspects::COLOR;
1384         }
1385 
1386         for (region_index, region) in regions.iter().enumerate() {
1387             let &BufferImageCopy {
1388                 buffer_offset,
1389                 buffer_row_length,
1390                 buffer_image_height,
1391                 ref image_subresource,
1392                 image_offset,
1393                 image_extent,
1394                 _ne: _,
1395             } = region;
1396 
1397             // VUID-VkCopyBufferToImageInfo2-imageSubresource-01701
1398             if image_subresource.mip_level >= dst_image.mip_levels() {
1399                 return Err(CopyError::MipLevelsOutOfRange {
1400                     resource: CopyErrorResource::Destination,
1401                     region_index,
1402                     mip_levels_range_end: image_subresource.mip_level + 1,
1403                     image_mip_levels: dst_image.mip_levels(),
1404                 });
1405             }
1406 
1407             // VUID-VkImageSubresourceLayers-layerCount-01700
1408             // VUID-VkCopyBufferToImageInfo2-baseArrayLayer-00213
1409             assert!(!image_subresource.array_layers.is_empty());
1410 
1411             // VUID-VkCopyBufferToImageInfo2-imageSubresource-01702
1412             // VUID-VkCopyBufferToImageInfo2-baseArrayLayer-00213
1413             if image_subresource.array_layers.end > dst_image.dimensions().array_layers() {
1414                 return Err(CopyError::ArrayLayersOutOfRange {
1415                     resource: CopyErrorResource::Destination,
1416                     region_index,
1417                     array_layers_range_end: image_subresource.array_layers.end,
1418                     image_array_layers: dst_image.dimensions().array_layers(),
1419                 });
1420             }
1421 
1422             // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask
1423             assert!(!image_subresource.aspects.is_empty());
1424 
1425             // VUID-VkCopyBufferToImageInfo2-aspectMask-00211
1426             if !image_aspects.contains(image_subresource.aspects) {
1427                 return Err(CopyError::AspectsNotAllowed {
1428                     resource: CopyErrorResource::Destination,
1429                     region_index,
1430                     aspects: image_subresource.aspects,
1431                     allowed_aspects: image_aspects,
1432                 });
1433             }
1434 
1435             // VUID-VkBufferImageCopy2-aspectMask-00212
1436             // VUID-VkCopyBufferToImageInfo2-aspectMask-01560
1437             if image_subresource.aspects.count() != 1 {
1438                 return Err(CopyError::MultipleAspectsNotAllowed {
1439                     resource: CopyErrorResource::Destination,
1440                     region_index,
1441                     aspects: image_subresource.aspects,
1442                 });
1443             }
1444 
1445             let (image_subresource_format, image_subresource_extent) =
1446                 if image_aspects.intersects(ImageAspects::PLANE_0) {
1447                     if image_subresource.aspects.intersects(ImageAspects::PLANE_0) {
1448                         (
1449                             dst_image.format().planes()[0],
1450                             dst_image.dimensions().width_height_depth(),
1451                         )
1452                     } else if image_subresource.aspects.intersects(ImageAspects::PLANE_1) {
1453                         (
1454                             dst_image.format().planes()[1],
1455                             dst_image
1456                                 .format()
1457                                 .ycbcr_chroma_sampling()
1458                                 .unwrap()
1459                                 .subsampled_extent(dst_image.dimensions().width_height_depth()),
1460                         )
1461                     } else {
1462                         (
1463                             dst_image.format().planes()[2],
1464                             dst_image
1465                                 .format()
1466                                 .ycbcr_chroma_sampling()
1467                                 .unwrap()
1468                                 .subsampled_extent(dst_image.dimensions().width_height_depth()),
1469                         )
1470                     }
1471                 } else {
1472                     (
1473                         dst_image.format(),
1474                         dst_image
1475                             .dimensions()
1476                             .mip_level_dimensions(image_subresource.mip_level)
1477                             .unwrap()
1478                             .width_height_depth(),
1479                     )
1480                 };
1481 
1482             if let Some(extent_alignment) = extent_alignment {
1483                 for i in 0..3 {
1484                     // VUID-VkBufferImageCopy2-imageExtent-06659
1485                     // VUID-VkBufferImageCopy2-imageExtent-06660
1486                     // VUID-VkBufferImageCopy2-imageExtent-06661
1487                     assert!(image_extent[i] != 0);
1488 
1489                     // VUID-VkCopyBufferToImageInfo2-pRegions-06223
1490                     // VUID-VkCopyBufferToImageInfo2-pRegions-06224
1491                     // VUID-VkCopyBufferToImageInfo2-imageOffset-00200
1492                     if image_offset[i] + image_extent[i] > image_subresource_extent[i] {
1493                         return Err(CopyError::RegionOutOfImageBounds {
1494                             resource: CopyErrorResource::Destination,
1495                             region_index,
1496                             offset_range_end: [
1497                                 image_offset[0] + image_extent[0],
1498                                 image_offset[1] + image_extent[1],
1499                                 image_offset[2] + image_extent[2],
1500                             ],
1501                             subresource_extent: image_subresource_extent,
1502                         });
1503                     }
1504 
1505                     // VUID-VkCopyBufferToImageInfo2-imageOffset-01793
1506                     // VUID-VkCopyBufferToImageInfo2-imageOffset-00205
1507                     if image_offset[i] % extent_alignment[i] != 0 {
1508                         return Err(CopyError::OffsetNotAlignedForImage {
1509                             resource: CopyErrorResource::Destination,
1510                             region_index,
1511                             offset: image_offset,
1512                             required_alignment: extent_alignment,
1513                         });
1514                     }
1515 
1516                     // VUID-VkCopyBufferToImageInfo2-imageOffset-01793
1517                     // VUID-VkCopyBufferToImageInfo2-imageExtent-00207
1518                     // VUID-VkCopyBufferToImageInfo2-imageExtent-00208
1519                     // VUID-VkCopyBufferToImageInfo2-imageExtent-00209
1520                     if image_offset[i] + image_extent[i] != image_subresource_extent[i]
1521                         && image_extent[i] % extent_alignment[i] != 0
1522                     {
1523                         return Err(CopyError::ExtentNotAlignedForImage {
1524                             resource: CopyErrorResource::Destination,
1525                             region_index,
1526                             extent: image_extent,
1527                             required_alignment: extent_alignment,
1528                         });
1529                     }
1530                 }
1531             } else {
1532                 // If granularity is `None`, then we can only copy whole subresources.
1533 
1534                 // VUID-VkCopyBufferToImageInfo2-imageOffset-01793
1535                 if image_offset != [0, 0, 0] {
1536                     return Err(CopyError::OffsetNotAlignedForImage {
1537                         resource: CopyErrorResource::Destination,
1538                         region_index,
1539                         offset: image_offset,
1540                         required_alignment: image_subresource_extent,
1541                     });
1542                 }
1543 
1544                 // VUID-VkCopyBufferToImageInfo2-imageOffset-01793
1545                 if image_extent != image_subresource_extent {
1546                     return Err(CopyError::ExtentNotAlignedForImage {
1547                         resource: CopyErrorResource::Destination,
1548                         region_index,
1549                         extent: image_extent,
1550                         required_alignment: image_subresource_extent,
1551                     });
1552                 }
1553             }
1554 
1555             // VUID-VkBufferImageCopy2-bufferRowLength-00195
1556             if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) {
1557                 return Err(CopyError::BufferRowLengthTooSmall {
1558                     resource: CopyErrorResource::Source,
1559                     region_index,
1560                     row_length: buffer_row_length,
1561                     min: image_extent[0],
1562                 });
1563             }
1564 
1565             // VUID-VkBufferImageCopy2-bufferImageHeight-00196
1566             if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) {
1567                 return Err(CopyError::BufferImageHeightTooSmall {
1568                     resource: CopyErrorResource::Source,
1569                     region_index,
1570                     image_height: buffer_image_height,
1571                     min: image_extent[1],
1572                 });
1573             }
1574 
1575             let image_subresource_block_extent = image_subresource_format.block_extent();
1576 
1577             // VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203
1578             if buffer_row_length % image_subresource_block_extent[0] != 0 {
1579                 return Err(CopyError::BufferRowLengthNotAligned {
1580                     resource: CopyErrorResource::Source,
1581                     region_index,
1582                     row_length: buffer_row_length,
1583                     required_alignment: image_subresource_block_extent[0],
1584                 });
1585             }
1586 
1587             // VUID-VkCopyBufferToImageInfo2-bufferImageHeight-00204
1588             if buffer_image_height % image_subresource_block_extent[1] != 0 {
1589                 return Err(CopyError::BufferImageHeightNotAligned {
1590                     resource: CopyErrorResource::Source,
1591                     region_index,
1592                     image_height: buffer_image_height,
1593                     required_alignment: image_subresource_block_extent[1],
1594                 });
1595             }
1596 
1597             // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferImageCopy.html#_description
1598             let image_subresource_block_size =
1599                 if image_subresource.aspects.intersects(ImageAspects::STENCIL) {
1600                     1
1601                 } else if image_subresource.aspects.intersects(ImageAspects::DEPTH) {
1602                     match image_subresource_format {
1603                         Format::D16_UNORM | Format::D16_UNORM_S8_UINT => 2,
1604                         Format::D32_SFLOAT
1605                         | Format::D32_SFLOAT_S8_UINT
1606                         | Format::X8_D24_UNORM_PACK32
1607                         | Format::D24_UNORM_S8_UINT => 4,
1608                         _ => unreachable!(),
1609                     }
1610                 } else {
1611                     image_subresource_format.block_size().unwrap()
1612                 };
1613 
1614             // VUID-VkCopyBufferToImageInfo2-pRegions-04725
1615             // VUID-VkCopyBufferToImageInfo2-pRegions-04726
1616             if (buffer_row_length / image_subresource_block_extent[0]) as DeviceSize
1617                 * image_subresource_block_size
1618                 > 0x7FFFFFFF
1619             {
1620                 return Err(CopyError::BufferRowLengthTooLarge {
1621                     resource: CopyErrorResource::Source,
1622                     region_index,
1623                     buffer_row_length,
1624                 });
1625             }
1626 
1627             let buffer_offset_alignment =
1628                 if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
1629                     4
1630                 } else {
1631                     let mut buffer_offset_alignment = image_subresource_block_size;
1632 
1633                     // VUID-VkCopyBufferToImageInfo2-commandBuffer-04052
1634                     // Make the alignment a multiple of 4.
1635                     if !queue_family_properties
1636                         .queue_flags
1637                         .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
1638                     {
1639                         if buffer_offset_alignment % 2 != 0 {
1640                             buffer_offset_alignment *= 2;
1641                         }
1642 
1643                         if buffer_offset_alignment % 4 != 0 {
1644                             buffer_offset_alignment *= 2;
1645                         }
1646                     }
1647 
1648                     buffer_offset_alignment
1649                 };
1650 
1651             // VUID-VkCopyBufferToImageInfo2-bufferOffset-00206
1652             // VUID-VkCopyBufferToImageInfo2-bufferOffset-01558
1653             // VUID-VkCopyBufferToImageInfo2-bufferOffset-01559
1654             // VUID-VkCopyBufferToImageInfo2-srcImage-04053
1655             if (src_buffer.offset() + buffer_offset) % buffer_offset_alignment != 0 {
1656                 return Err(CopyError::OffsetNotAlignedForBuffer {
1657                     resource: CopyErrorResource::Source,
1658                     region_index,
1659                     offset: src_buffer.offset() + buffer_offset,
1660                     required_alignment: buffer_offset_alignment,
1661                 });
1662             }
1663 
1664             let buffer_copy_size = region.buffer_copy_size(image_subresource_format);
1665 
1666             // VUID-VkCopyBufferToImageInfo2-pRegions-00171
1667             if buffer_offset + buffer_copy_size > src_buffer.size() {
1668                 return Err(CopyError::RegionOutOfBufferBounds {
1669                     resource: CopyErrorResource::Source,
1670                     region_index,
1671                     offset_range_end: buffer_offset + buffer_copy_size,
1672                     buffer_size: src_buffer.size(),
1673                 });
1674             }
1675         }
1676 
1677         // VUID-VkCopyBufferToImageInfo2-pRegions-00173
1678         // Can't occur as long as memory aliasing isn't allowed.
1679 
1680         Ok(())
1681     }
1682 
1683     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
copy_buffer_to_image_unchecked( &mut self, copy_buffer_to_image_info: CopyBufferToImageInfo, ) -> &mut Self1684     pub unsafe fn copy_buffer_to_image_unchecked(
1685         &mut self,
1686         copy_buffer_to_image_info: CopyBufferToImageInfo,
1687     ) -> &mut Self {
1688         let CopyBufferToImageInfo {
1689             src_buffer,
1690             dst_image,
1691             dst_image_layout,
1692             regions,
1693             _ne: _,
1694         } = copy_buffer_to_image_info;
1695 
1696         if regions.is_empty() {
1697             return self;
1698         }
1699 
1700         let dst_image_inner = dst_image.inner();
1701 
1702         let fns = self.device().fns();
1703 
1704         if self.device().api_version() >= Version::V1_3
1705             || self.device().enabled_extensions().khr_copy_commands2
1706         {
1707             let regions: SmallVec<[_; 8]> = regions
1708                 .iter()
1709                 .map(|region| {
1710                     let &BufferImageCopy {
1711                         buffer_offset,
1712                         buffer_row_length,
1713                         buffer_image_height,
1714                         ref image_subresource,
1715                         image_offset,
1716                         image_extent,
1717                         _ne: _,
1718                     } = region;
1719 
1720                     let mut image_subresource = image_subresource.clone();
1721                     image_subresource.array_layers.start += dst_image_inner.first_layer;
1722                     image_subresource.array_layers.end += dst_image_inner.first_layer;
1723                     image_subresource.mip_level += dst_image_inner.first_mipmap_level;
1724 
1725                     ash::vk::BufferImageCopy2 {
1726                         buffer_offset: buffer_offset + src_buffer.offset(),
1727                         buffer_row_length,
1728                         buffer_image_height,
1729                         image_subresource: image_subresource.into(),
1730                         image_offset: ash::vk::Offset3D {
1731                             x: image_offset[0] as i32,
1732                             y: image_offset[1] as i32,
1733                             z: image_offset[2] as i32,
1734                         },
1735                         image_extent: ash::vk::Extent3D {
1736                             width: image_extent[0],
1737                             height: image_extent[1],
1738                             depth: image_extent[2],
1739                         },
1740                         ..Default::default()
1741                     }
1742                 })
1743                 .collect();
1744 
1745             let copy_buffer_to_image_info = ash::vk::CopyBufferToImageInfo2 {
1746                 src_buffer: src_buffer.buffer().handle(),
1747                 dst_image: dst_image_inner.image.handle(),
1748                 dst_image_layout: dst_image_layout.into(),
1749                 region_count: regions.len() as u32,
1750                 p_regions: regions.as_ptr(),
1751                 ..Default::default()
1752             };
1753 
1754             if self.device().api_version() >= Version::V1_3 {
1755                 (fns.v1_3.cmd_copy_buffer_to_image2)(self.handle(), &copy_buffer_to_image_info);
1756             } else {
1757                 (fns.khr_copy_commands2.cmd_copy_buffer_to_image2_khr)(
1758                     self.handle(),
1759                     &copy_buffer_to_image_info,
1760                 );
1761             }
1762         } else {
1763             let regions: SmallVec<[_; 8]> = regions
1764                 .iter()
1765                 .map(|region| {
1766                     let &BufferImageCopy {
1767                         buffer_offset,
1768                         buffer_row_length,
1769                         buffer_image_height,
1770                         ref image_subresource,
1771                         image_offset,
1772                         image_extent,
1773                         _ne: _,
1774                     } = region;
1775 
1776                     let mut image_subresource = image_subresource.clone();
1777                     image_subresource.array_layers.start += dst_image_inner.first_layer;
1778                     image_subresource.array_layers.end += dst_image_inner.first_layer;
1779                     image_subresource.mip_level += dst_image_inner.first_mipmap_level;
1780 
1781                     ash::vk::BufferImageCopy {
1782                         buffer_offset: buffer_offset + src_buffer.offset(),
1783                         buffer_row_length,
1784                         buffer_image_height,
1785                         image_subresource: image_subresource.into(),
1786                         image_offset: ash::vk::Offset3D {
1787                             x: image_offset[0] as i32,
1788                             y: image_offset[1] as i32,
1789                             z: image_offset[2] as i32,
1790                         },
1791                         image_extent: ash::vk::Extent3D {
1792                             width: image_extent[0],
1793                             height: image_extent[1],
1794                             depth: image_extent[2],
1795                         },
1796                     }
1797                 })
1798                 .collect();
1799 
1800             (fns.v1_0.cmd_copy_buffer_to_image)(
1801                 self.handle(),
1802                 src_buffer.buffer().handle(),
1803                 dst_image_inner.image.handle(),
1804                 dst_image_layout.into(),
1805                 regions.len() as u32,
1806                 regions.as_ptr(),
1807             );
1808         }
1809 
1810         let command_index = self.next_command_index;
1811         let command_name = "copy_buffer_to_image";
1812         let src_use_ref = ResourceUseRef {
1813             command_index,
1814             command_name,
1815             resource_in_command: ResourceInCommand::Source,
1816             secondary_use_ref: None,
1817         };
1818         let dst_use_ref = ResourceUseRef {
1819             command_index,
1820             command_name,
1821             resource_in_command: ResourceInCommand::Destination,
1822             secondary_use_ref: None,
1823         };
1824 
1825         for region in regions {
1826             let buffer_copy_size = region.buffer_copy_size(dst_image.format());
1827 
1828             let BufferImageCopy {
1829                 buffer_offset,
1830                 buffer_row_length: _,
1831                 buffer_image_height: _,
1832                 image_subresource,
1833                 image_offset: _,
1834                 image_extent: _,
1835                 _ne: _,
1836             } = region;
1837 
1838             let mut src_range = buffer_offset..buffer_offset + buffer_copy_size;
1839             src_range.start += src_buffer.offset();
1840             src_range.end += src_buffer.offset();
1841             self.resources_usage_state.record_buffer_access(
1842                 &src_use_ref,
1843                 src_buffer.buffer(),
1844                 src_range,
1845                 PipelineStageAccess::Copy_TransferRead,
1846             );
1847 
1848             let mut dst_subresource_range = ImageSubresourceRange::from(image_subresource);
1849             dst_subresource_range.array_layers.start += dst_image_inner.first_layer;
1850             dst_subresource_range.array_layers.end += dst_image_inner.first_layer;
1851             dst_subresource_range.mip_levels.start += dst_image_inner.first_mipmap_level;
1852             dst_subresource_range.mip_levels.end += dst_image_inner.first_mipmap_level;
1853             self.resources_usage_state.record_image_access(
1854                 &dst_use_ref,
1855                 dst_image_inner.image,
1856                 dst_subresource_range,
1857                 PipelineStageAccess::Copy_TransferWrite,
1858                 dst_image_layout,
1859             );
1860         }
1861 
1862         self.resources.push(Box::new(src_buffer));
1863         self.resources.push(Box::new(dst_image));
1864 
1865         self.next_command_index += 1;
1866         self
1867     }
1868 
1869     /// Copies from an image to a buffer.
1870     ///
1871     /// # Safety
1872     ///
1873     /// - Appropriate synchronization must be provided for all buffers and images
1874     ///   that are accessed by the command.
1875     /// - All images that are accessed by the command must be in the expected image layout.
1876     #[inline]
copy_image_to_buffer( &mut self, copy_image_to_buffer_info: CopyImageToBufferInfo, ) -> Result<&mut Self, CopyError>1877     pub unsafe fn copy_image_to_buffer(
1878         &mut self,
1879         copy_image_to_buffer_info: CopyImageToBufferInfo,
1880     ) -> Result<&mut Self, CopyError> {
1881         self.validate_copy_image_to_buffer(&copy_image_to_buffer_info)?;
1882 
1883         unsafe { Ok(self.copy_image_to_buffer_unchecked(copy_image_to_buffer_info)) }
1884     }
1885 
validate_copy_image_to_buffer( &self, copy_image_to_buffer_info: &CopyImageToBufferInfo, ) -> Result<(), CopyError>1886     fn validate_copy_image_to_buffer(
1887         &self,
1888         copy_image_to_buffer_info: &CopyImageToBufferInfo,
1889     ) -> Result<(), CopyError> {
1890         let device = self.device();
1891 
1892         // VUID-vkCmdCopyImageToBuffer2-renderpass
1893         if self.builder_state.render_pass.is_some() {
1894             return Err(CopyError::ForbiddenInsideRenderPass);
1895         }
1896 
1897         let queue_family_properties = self.queue_family_properties();
1898 
1899         // VUID-vkCmdCopyImageToBuffer2-commandBuffer-cmdpool
1900         if !queue_family_properties
1901             .queue_flags
1902             .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
1903         {
1904             return Err(CopyError::NotSupportedByQueueFamily);
1905         }
1906 
1907         let &CopyImageToBufferInfo {
1908             ref src_image,
1909             src_image_layout,
1910             ref dst_buffer,
1911             ref regions,
1912             _ne: _,
1913         } = copy_image_to_buffer_info;
1914 
1915         // VUID-VkCopyImageToBufferInfo2-srcImageLayout-parameter
1916         src_image_layout.validate_device(device)?;
1917 
1918         // VUID-VkCopyImageToBufferInfo2-commonparent
1919         assert_eq!(device, dst_buffer.device());
1920         assert_eq!(device, src_image.device());
1921 
1922         let mut image_aspects = src_image.format().aspects();
1923 
1924         // VUID-VkCopyImageToBufferInfo2-srcImage-00186
1925         if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) {
1926             return Err(CopyError::MissingUsage {
1927                 resource: CopyErrorResource::Source,
1928                 usage: "transfer_src",
1929             });
1930         }
1931 
1932         // VUID-VkCopyImageToBufferInfo2-dstBuffer-00191
1933         if !dst_buffer
1934             .buffer()
1935             .usage()
1936             .intersects(BufferUsage::TRANSFER_DST)
1937         {
1938             return Err(CopyError::MissingUsage {
1939                 resource: CopyErrorResource::Destination,
1940                 usage: "transfer_dst",
1941             });
1942         }
1943 
1944         if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
1945             // VUID-VkCopyImageToBufferInfo2-srcImage-01998
1946             if !src_image
1947                 .format_features()
1948                 .intersects(FormatFeatures::TRANSFER_SRC)
1949             {
1950                 return Err(CopyError::MissingFormatFeature {
1951                     resource: CopyErrorResource::Source,
1952                     format_feature: "transfer_src",
1953                 });
1954             }
1955         }
1956 
1957         // VUID-VkCopyImageToBufferInfo2-srcImage-00188
1958         if src_image.samples() != SampleCount::Sample1 {
1959             return Err(CopyError::SampleCountInvalid {
1960                 resource: CopyErrorResource::Source,
1961                 sample_count: src_image.samples(),
1962                 allowed_sample_counts: SampleCounts::SAMPLE_1,
1963             });
1964         }
1965 
1966         // VUID-VkCopyImageToBufferInfo2-srcImageLayout-01397
1967         if !matches!(
1968             src_image_layout,
1969             ImageLayout::TransferSrcOptimal | ImageLayout::General
1970         ) {
1971             return Err(CopyError::ImageLayoutInvalid {
1972                 resource: CopyErrorResource::Source,
1973                 image_layout: src_image_layout,
1974             });
1975         }
1976 
1977         let extent_alignment = match queue_family_properties.min_image_transfer_granularity {
1978             [0, 0, 0] => None,
1979             min_image_transfer_granularity => {
1980                 let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| {
1981                     if is_multi_plane {
1982                         // Assume planes always have 1x1 blocks
1983                         min_image_transfer_granularity
1984                     } else {
1985                         // "The value returned in minImageTransferGranularity has a unit of
1986                         // compressed texel blocks for images having a block-compressed format, and
1987                         // a unit of texels otherwise."
1988                         [
1989                             min_image_transfer_granularity[0] * block_extent[0],
1990                             min_image_transfer_granularity[1] * block_extent[1],
1991                             min_image_transfer_granularity[2] * block_extent[2],
1992                         ]
1993                     }
1994                 };
1995 
1996                 Some(granularity(
1997                     src_image.format().block_extent(),
1998                     image_aspects.intersects(ImageAspects::PLANE_0),
1999                 ))
2000             }
2001         };
2002 
2003         if image_aspects.intersects(ImageAspects::PLANE_0) {
2004             // VUID-VkCopyImageToBufferInfo2-aspectMask-01560
2005             image_aspects -= ImageAspects::COLOR;
2006         }
2007 
2008         for (region_index, region) in regions.iter().enumerate() {
2009             let &BufferImageCopy {
2010                 buffer_offset,
2011                 buffer_row_length,
2012                 buffer_image_height,
2013                 ref image_subresource,
2014                 image_offset,
2015                 image_extent,
2016                 _ne: _,
2017             } = region;
2018 
2019             // VUID-VkCopyImageToBufferInfo2-imageSubresource-01703
2020             if image_subresource.mip_level >= src_image.mip_levels() {
2021                 return Err(CopyError::MipLevelsOutOfRange {
2022                     resource: CopyErrorResource::Source,
2023                     region_index,
2024                     mip_levels_range_end: image_subresource.mip_level + 1,
2025                     image_mip_levels: src_image.mip_levels(),
2026                 });
2027             }
2028 
2029             // VUID-VkImageSubresourceLayers-layerCount-01700
2030             assert!(!image_subresource.array_layers.is_empty());
2031 
2032             // VUID-VkCopyImageToBufferInfo2-imageSubresource-01704
2033             // VUID-VkCopyImageToBufferInfo2-baseArrayLayer-00213
2034             if image_subresource.array_layers.end > src_image.dimensions().array_layers() {
2035                 return Err(CopyError::ArrayLayersOutOfRange {
2036                     resource: CopyErrorResource::Source,
2037                     region_index,
2038                     array_layers_range_end: image_subresource.array_layers.end,
2039                     image_array_layers: src_image.dimensions().array_layers(),
2040                 });
2041             }
2042 
2043             // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask
2044             assert!(!image_subresource.aspects.is_empty());
2045 
2046             // VUID-VkCopyImageToBufferInfo2-aspectMask-00211
2047             if !image_aspects.contains(image_subresource.aspects) {
2048                 return Err(CopyError::AspectsNotAllowed {
2049                     resource: CopyErrorResource::Source,
2050                     region_index,
2051                     aspects: image_subresource.aspects,
2052                     allowed_aspects: image_aspects,
2053                 });
2054             }
2055 
2056             // VUID-VkBufferImageCopy2-aspectMask-00212
2057             if image_subresource.aspects.count() != 1 {
2058                 return Err(CopyError::MultipleAspectsNotAllowed {
2059                     resource: CopyErrorResource::Source,
2060                     region_index,
2061                     aspects: image_subresource.aspects,
2062                 });
2063             }
2064 
2065             let (image_subresource_format, image_subresource_extent) =
2066                 if image_aspects.intersects(ImageAspects::PLANE_0) {
2067                     if image_subresource.aspects.intersects(ImageAspects::PLANE_0) {
2068                         (
2069                             src_image.format().planes()[0],
2070                             src_image.dimensions().width_height_depth(),
2071                         )
2072                     } else if image_subresource.aspects.intersects(ImageAspects::PLANE_1) {
2073                         (
2074                             src_image.format().planes()[1],
2075                             src_image
2076                                 .format()
2077                                 .ycbcr_chroma_sampling()
2078                                 .unwrap()
2079                                 .subsampled_extent(src_image.dimensions().width_height_depth()),
2080                         )
2081                     } else {
2082                         (
2083                             src_image.format().planes()[2],
2084                             src_image
2085                                 .format()
2086                                 .ycbcr_chroma_sampling()
2087                                 .unwrap()
2088                                 .subsampled_extent(src_image.dimensions().width_height_depth()),
2089                         )
2090                     }
2091                 } else {
2092                     (
2093                         src_image.format(),
2094                         src_image
2095                             .dimensions()
2096                             .mip_level_dimensions(image_subresource.mip_level)
2097                             .unwrap()
2098                             .width_height_depth(),
2099                     )
2100                 };
2101 
2102             if let Some(extent_alignment) = extent_alignment {
2103                 for i in 0..3 {
2104                     // VUID-VkBufferImageCopy2-imageExtent-06659
2105                     // VUID-VkBufferImageCopy2-imageExtent-06660
2106                     // VUID-VkBufferImageCopy2-imageExtent-06661
2107                     assert!(image_extent[i] != 0);
2108 
2109                     // VUID-VkCopyImageToBufferInfo2-imageOffset-00197
2110                     // VUID-VkCopyImageToBufferInfo2-imageOffset-00198
2111                     // VUID-VkCopyImageToBufferInfo2-imageOffset-00200
2112                     if image_offset[i] + image_extent[i] > image_subresource_extent[i] {
2113                         return Err(CopyError::RegionOutOfImageBounds {
2114                             resource: CopyErrorResource::Source,
2115                             region_index,
2116                             offset_range_end: [
2117                                 image_offset[0] + image_extent[0],
2118                                 image_offset[1] + image_extent[1],
2119                                 image_offset[2] + image_extent[2],
2120                             ],
2121                             subresource_extent: image_subresource_extent,
2122                         });
2123                     }
2124 
2125                     // VUID-VkCopyImageToBufferInfo2-imageOffset-01794
2126                     // VUID-VkCopyImageToBufferInfo2-imageOffset-00205
2127                     if image_offset[i] % extent_alignment[i] != 0 {
2128                         return Err(CopyError::OffsetNotAlignedForImage {
2129                             resource: CopyErrorResource::Source,
2130                             region_index,
2131                             offset: image_offset,
2132                             required_alignment: extent_alignment,
2133                         });
2134                     }
2135 
2136                     // VUID-VkCopyImageToBufferInfo2-imageOffset-01794
2137                     // VUID-VkCopyImageToBufferInfo2-imageExtent-00207
2138                     // VUID-VkCopyImageToBufferInfo2-imageExtent-00208
2139                     // VUID-VkCopyImageToBufferInfo2-imageExtent-00209
2140                     if image_offset[i] + image_extent[i] != image_subresource_extent[i]
2141                         && image_extent[i] % extent_alignment[i] != 0
2142                     {
2143                         return Err(CopyError::ExtentNotAlignedForImage {
2144                             resource: CopyErrorResource::Source,
2145                             region_index,
2146                             extent: image_extent,
2147                             required_alignment: extent_alignment,
2148                         });
2149                     }
2150                 }
2151             } else {
2152                 // If granularity is `None`, then we can only copy whole subresources.
2153 
2154                 // VUID-VkCopyBufferToImageInfo2-imageOffset-01793
2155                 if image_offset != [0, 0, 0] {
2156                     return Err(CopyError::OffsetNotAlignedForImage {
2157                         resource: CopyErrorResource::Source,
2158                         region_index,
2159                         offset: image_offset,
2160                         required_alignment: image_subresource_extent,
2161                     });
2162                 }
2163 
2164                 // VUID-VkCopyBufferToImageInfo2-imageOffset-01793
2165                 if image_extent != image_subresource_extent {
2166                     return Err(CopyError::ExtentNotAlignedForImage {
2167                         resource: CopyErrorResource::Source,
2168                         region_index,
2169                         extent: image_extent,
2170                         required_alignment: image_subresource_extent,
2171                     });
2172                 }
2173             }
2174 
2175             // VUID-VkBufferImageCopy2-bufferRowLength-00195
2176             if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) {
2177                 return Err(CopyError::BufferRowLengthTooSmall {
2178                     resource: CopyErrorResource::Destination,
2179                     region_index,
2180                     row_length: buffer_row_length,
2181                     min: image_extent[0],
2182                 });
2183             }
2184 
2185             // VUID-VkBufferImageCopy2-bufferImageHeight-00196
2186             if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) {
2187                 return Err(CopyError::BufferImageHeightTooSmall {
2188                     resource: CopyErrorResource::Destination,
2189                     region_index,
2190                     image_height: buffer_image_height,
2191                     min: image_extent[1],
2192                 });
2193             }
2194 
2195             let image_subresource_block_extent = image_subresource_format.block_extent();
2196 
2197             // VUID-VkCopyImageToBufferInfo2-bufferRowLength-00203
2198             if buffer_row_length % image_subresource_block_extent[0] != 0 {
2199                 return Err(CopyError::BufferRowLengthNotAligned {
2200                     resource: CopyErrorResource::Destination,
2201                     region_index,
2202                     row_length: buffer_row_length,
2203                     required_alignment: image_subresource_block_extent[0],
2204                 });
2205             }
2206 
2207             // VUID-VkCopyImageToBufferInfo2-bufferImageHeight-00204
2208             if buffer_image_height % image_subresource_block_extent[1] != 0 {
2209                 return Err(CopyError::BufferImageHeightNotAligned {
2210                     resource: CopyErrorResource::Destination,
2211                     region_index,
2212                     image_height: buffer_image_height,
2213                     required_alignment: image_subresource_block_extent[1],
2214                 });
2215             }
2216 
2217             // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferImageCopy.html#_description
2218             let image_subresource_block_size =
2219                 if image_subresource.aspects.intersects(ImageAspects::STENCIL) {
2220                     1
2221                 } else if image_subresource.aspects.intersects(ImageAspects::DEPTH) {
2222                     match image_subresource_format {
2223                         Format::D16_UNORM | Format::D16_UNORM_S8_UINT => 2,
2224                         Format::D32_SFLOAT
2225                         | Format::D32_SFLOAT_S8_UINT
2226                         | Format::X8_D24_UNORM_PACK32
2227                         | Format::D24_UNORM_S8_UINT => 4,
2228                         _ => unreachable!(),
2229                     }
2230                 } else {
2231                     image_subresource_format.block_size().unwrap()
2232                 };
2233 
2234             // VUID-VkCopyImageToBufferInfo2-pRegions-04725
2235             // VUID-VkCopyImageToBufferInfo2-pRegions-04726
2236             if (buffer_row_length / image_subresource_block_extent[0]) as DeviceSize
2237                 * image_subresource_block_size
2238                 > 0x7FFFFFFF
2239             {
2240                 return Err(CopyError::BufferRowLengthTooLarge {
2241                     resource: CopyErrorResource::Destination,
2242                     region_index,
2243                     buffer_row_length,
2244                 });
2245             }
2246 
2247             let buffer_offset_alignment =
2248                 if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
2249                     4
2250                 } else {
2251                     let mut buffer_offset_alignment = image_subresource_block_size;
2252 
2253                     // VUID-VkCopyImageToBufferInfo2-commandBuffer-04052
2254                     // Make the alignment a multiple of 4.
2255                     if !queue_family_properties
2256                         .queue_flags
2257                         .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
2258                     {
2259                         if buffer_offset_alignment % 2 != 0 {
2260                             buffer_offset_alignment *= 2;
2261                         }
2262 
2263                         if buffer_offset_alignment % 4 != 0 {
2264                             buffer_offset_alignment *= 2;
2265                         }
2266                     }
2267 
2268                     buffer_offset_alignment
2269                 };
2270 
2271             // VUID-VkCopyImageToBufferInfo2-bufferOffset-01558
2272             // VUID-VkCopyImageToBufferInfo2-bufferOffset-01559
2273             // VUID-VkCopyImageToBufferInfo2-bufferOffset-00206
2274             // VUID-VkCopyImageToBufferInfo2-srcImage-04053
2275             if (dst_buffer.offset() + buffer_offset) % buffer_offset_alignment != 0 {
2276                 return Err(CopyError::OffsetNotAlignedForBuffer {
2277                     resource: CopyErrorResource::Destination,
2278                     region_index,
2279                     offset: dst_buffer.offset() + buffer_offset,
2280                     required_alignment: buffer_offset_alignment,
2281                 });
2282             }
2283 
2284             let buffer_copy_size = region.buffer_copy_size(image_subresource_format);
2285 
2286             // VUID-VkCopyImageToBufferInfo2-pRegions-00183
2287             if buffer_offset + buffer_copy_size > dst_buffer.size() {
2288                 return Err(CopyError::RegionOutOfBufferBounds {
2289                     resource: CopyErrorResource::Destination,
2290                     region_index,
2291                     offset_range_end: buffer_offset + buffer_copy_size,
2292                     buffer_size: dst_buffer.size(),
2293                 });
2294             }
2295         }
2296 
2297         // VUID-VkCopyImageToBufferInfo2-pRegions-00184
2298         // Can't occur as long as memory aliasing isn't allowed.
2299 
2300         // TODO: sync check
2301 
2302         Ok(())
2303     }
2304 
2305     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
copy_image_to_buffer_unchecked( &mut self, copy_image_to_buffer_info: CopyImageToBufferInfo, ) -> &mut Self2306     pub unsafe fn copy_image_to_buffer_unchecked(
2307         &mut self,
2308         copy_image_to_buffer_info: CopyImageToBufferInfo,
2309     ) -> &mut Self {
2310         let CopyImageToBufferInfo {
2311             src_image,
2312             src_image_layout,
2313             dst_buffer,
2314             regions,
2315             _ne: _,
2316         } = copy_image_to_buffer_info;
2317 
2318         if regions.is_empty() {
2319             return self;
2320         }
2321 
2322         let src_image_inner = src_image.inner();
2323 
2324         let fns = self.device().fns();
2325 
2326         if self.device().api_version() >= Version::V1_3
2327             || self.device().enabled_extensions().khr_copy_commands2
2328         {
2329             let regions: SmallVec<[_; 8]> = regions
2330                 .iter()
2331                 .map(|region| {
2332                     let &BufferImageCopy {
2333                         buffer_offset,
2334                         buffer_row_length,
2335                         buffer_image_height,
2336                         ref image_subresource,
2337                         image_offset,
2338                         image_extent,
2339                         _ne: _,
2340                     } = region;
2341 
2342                     let mut image_subresource = image_subresource.clone();
2343                     image_subresource.array_layers.start += src_image_inner.first_layer;
2344                     image_subresource.array_layers.end += src_image_inner.first_layer;
2345                     image_subresource.mip_level += src_image_inner.first_mipmap_level;
2346 
2347                     ash::vk::BufferImageCopy2 {
2348                         buffer_offset: buffer_offset + dst_buffer.offset(),
2349                         buffer_row_length,
2350                         buffer_image_height,
2351                         image_subresource: image_subresource.into(),
2352                         image_offset: ash::vk::Offset3D {
2353                             x: image_offset[0] as i32,
2354                             y: image_offset[1] as i32,
2355                             z: image_offset[2] as i32,
2356                         },
2357                         image_extent: ash::vk::Extent3D {
2358                             width: image_extent[0],
2359                             height: image_extent[1],
2360                             depth: image_extent[2],
2361                         },
2362                         ..Default::default()
2363                     }
2364                 })
2365                 .collect();
2366 
2367             let copy_image_to_buffer_info = ash::vk::CopyImageToBufferInfo2 {
2368                 src_image: src_image_inner.image.handle(),
2369                 src_image_layout: src_image_layout.into(),
2370                 dst_buffer: dst_buffer.buffer().handle(),
2371                 region_count: regions.len() as u32,
2372                 p_regions: regions.as_ptr(),
2373                 ..Default::default()
2374             };
2375 
2376             if self.device().api_version() >= Version::V1_3 {
2377                 (fns.v1_3.cmd_copy_image_to_buffer2)(self.handle(), &copy_image_to_buffer_info);
2378             } else {
2379                 (fns.khr_copy_commands2.cmd_copy_image_to_buffer2_khr)(
2380                     self.handle(),
2381                     &copy_image_to_buffer_info,
2382                 );
2383             }
2384         } else {
2385             let regions: SmallVec<[_; 8]> = regions
2386                 .iter()
2387                 .map(|region| {
2388                     let &BufferImageCopy {
2389                         buffer_offset,
2390                         buffer_row_length,
2391                         buffer_image_height,
2392                         ref image_subresource,
2393                         image_offset,
2394                         image_extent,
2395                         _ne: _,
2396                     } = region;
2397                     let mut image_subresource = image_subresource.clone();
2398                     image_subresource.array_layers.start += src_image_inner.first_layer;
2399                     image_subresource.array_layers.end += src_image_inner.first_layer;
2400                     image_subresource.mip_level += src_image_inner.first_mipmap_level;
2401 
2402                     ash::vk::BufferImageCopy {
2403                         buffer_offset: buffer_offset + dst_buffer.offset(),
2404                         buffer_row_length,
2405                         buffer_image_height,
2406                         image_subresource: image_subresource.into(),
2407                         image_offset: ash::vk::Offset3D {
2408                             x: image_offset[0] as i32,
2409                             y: image_offset[1] as i32,
2410                             z: image_offset[2] as i32,
2411                         },
2412                         image_extent: ash::vk::Extent3D {
2413                             width: image_extent[0],
2414                             height: image_extent[1],
2415                             depth: image_extent[2],
2416                         },
2417                     }
2418                 })
2419                 .collect();
2420 
2421             (fns.v1_0.cmd_copy_image_to_buffer)(
2422                 self.handle(),
2423                 src_image_inner.image.handle(),
2424                 src_image_layout.into(),
2425                 dst_buffer.buffer().handle(),
2426                 regions.len() as u32,
2427                 regions.as_ptr(),
2428             );
2429         }
2430 
2431         let command_index = self.next_command_index;
2432         let command_name = "copy_image_to_buffer";
2433         let src_use_ref = ResourceUseRef {
2434             command_index,
2435             command_name,
2436             resource_in_command: ResourceInCommand::Source,
2437             secondary_use_ref: None,
2438         };
2439         let dst_use_ref = ResourceUseRef {
2440             command_index,
2441             command_name,
2442             resource_in_command: ResourceInCommand::Destination,
2443             secondary_use_ref: None,
2444         };
2445 
2446         for region in regions {
2447             let buffer_copy_size = region.buffer_copy_size(src_image.format());
2448 
2449             let BufferImageCopy {
2450                 buffer_offset,
2451                 buffer_row_length: _,
2452                 buffer_image_height: _,
2453                 image_subresource,
2454                 image_offset: _,
2455                 image_extent: _,
2456                 _ne: _,
2457             } = region;
2458 
2459             let mut src_subresource_range = ImageSubresourceRange::from(image_subresource);
2460             src_subresource_range.array_layers.start += src_image_inner.first_layer;
2461             src_subresource_range.array_layers.end += src_image_inner.first_layer;
2462             src_subresource_range.mip_levels.start += src_image_inner.first_mipmap_level;
2463             src_subresource_range.mip_levels.end += src_image_inner.first_mipmap_level;
2464             self.resources_usage_state.record_image_access(
2465                 &src_use_ref,
2466                 src_image_inner.image,
2467                 src_subresource_range,
2468                 PipelineStageAccess::Copy_TransferRead,
2469                 src_image_layout,
2470             );
2471 
2472             let mut dst_range = buffer_offset..buffer_offset + buffer_copy_size;
2473             dst_range.start += dst_buffer.offset();
2474             dst_range.end += dst_buffer.offset();
2475             self.resources_usage_state.record_buffer_access(
2476                 &dst_use_ref,
2477                 dst_buffer.buffer(),
2478                 dst_range,
2479                 PipelineStageAccess::Copy_TransferWrite,
2480             );
2481         }
2482 
2483         self.resources.push(Box::new(src_image));
2484         self.resources.push(Box::new(dst_buffer));
2485 
2486         self.next_command_index += 1;
2487         self
2488     }
2489 
2490     /// Blits an image to another.
2491     ///
2492     /// A *blit* is similar to an image copy operation, except that the portion of the image that
2493     /// is transferred can be resized. You choose an area of the source and an area of the
2494     /// destination, and the implementation will resize the area of the source so that it matches
2495     /// the size of the area of the destination before writing it.
2496     ///
2497     /// Blit operations have several restrictions:
2498     ///
2499     /// - Blit operations are only allowed on queue families that support graphics operations.
2500     /// - The format of the source and destination images must support blit operations, which
2501     ///   depends on the Vulkan implementation. Vulkan guarantees that some specific formats must
2502     ///   always be supported. See tables 52 to 61 of the specifications.
2503     /// - Only single-sampled images are allowed.
2504     /// - You can only blit between two images whose formats belong to the same type. The types
2505     ///   are: floating-point, signed integers, unsigned integers, depth-stencil.
2506     /// - If you blit between depth, stencil or depth-stencil images, the format of both images
2507     ///   must match exactly.
2508     /// - If you blit between depth, stencil or depth-stencil images, only the `Nearest` filter is
2509     ///   allowed.
2510     /// - For two-dimensional images, the Z coordinate must be 0 for the top-left offset and 1 for
2511     ///   the bottom-right offset. Same for the Y coordinate for one-dimensional images.
2512     /// - For non-array images, the base array layer must be 0 and the number of layers must be 1.
2513     ///
2514     /// If `layer_count` is greater than 1, the blit will happen between each individual layer as
2515     /// if they were separate images.
2516     ///
2517     /// # Panic
2518     ///
2519     /// - Panics if the source or the destination was not created with `device`.
2520     ///
2521     /// # Safety
2522     ///
2523     /// - Appropriate synchronization must be provided for all images
2524     ///   that are accessed by the command.
2525     /// - All images that are accessed by the command must be in the expected image layout.
2526     #[inline]
blit_image( &mut self, blit_image_info: BlitImageInfo, ) -> Result<&mut Self, CopyError>2527     pub unsafe fn blit_image(
2528         &mut self,
2529         blit_image_info: BlitImageInfo,
2530     ) -> Result<&mut Self, CopyError> {
2531         self.validate_blit_image(&blit_image_info)?;
2532 
2533         unsafe { Ok(self.blit_image_unchecked(blit_image_info)) }
2534     }
2535 
validate_blit_image(&self, blit_image_info: &BlitImageInfo) -> Result<(), CopyError>2536     fn validate_blit_image(&self, blit_image_info: &BlitImageInfo) -> Result<(), CopyError> {
2537         let device = self.device();
2538 
2539         // VUID-vkCmdBlitImage2-renderpass
2540         if self.builder_state.render_pass.is_some() {
2541             return Err(CopyError::ForbiddenInsideRenderPass);
2542         }
2543 
2544         let queue_family_properties = self.queue_family_properties();
2545 
2546         // VUID-vkCmdBlitImage2-commandBuffer-cmdpool
2547         if !queue_family_properties
2548             .queue_flags
2549             .intersects(QueueFlags::GRAPHICS)
2550         {
2551             return Err(CopyError::NotSupportedByQueueFamily);
2552         }
2553 
2554         let &BlitImageInfo {
2555             ref src_image,
2556             src_image_layout,
2557             ref dst_image,
2558             dst_image_layout,
2559             ref regions,
2560             filter,
2561             _ne: _,
2562         } = blit_image_info;
2563 
2564         // VUID-VkBlitImageInfo2-srcImageLayout-parameter
2565         src_image_layout.validate_device(device)?;
2566 
2567         // VUID-VkBlitImageInfo2-dstImageLayout-parameter
2568         dst_image_layout.validate_device(device)?;
2569 
2570         // VUID-VkBlitImageInfo2-filter-parameter
2571         filter.validate_device(device)?;
2572 
2573         let src_image_inner = src_image.inner();
2574         let dst_image_inner = dst_image.inner();
2575 
2576         // VUID-VkBlitImageInfo2-commonparent
2577         assert_eq!(device, src_image.device());
2578         assert_eq!(device, dst_image.device());
2579 
2580         let src_image_aspects = src_image.format().aspects();
2581         let dst_image_aspects = dst_image.format().aspects();
2582         let src_image_type = src_image.dimensions().image_type();
2583         let dst_image_type = dst_image.dimensions().image_type();
2584 
2585         // VUID-VkBlitImageInfo2-srcImage-00219
2586         if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) {
2587             return Err(CopyError::MissingUsage {
2588                 resource: CopyErrorResource::Source,
2589                 usage: "transfer_src",
2590             });
2591         }
2592 
2593         // VUID-VkBlitImageInfo2-dstImage-00224
2594         if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) {
2595             return Err(CopyError::MissingUsage {
2596                 resource: CopyErrorResource::Destination,
2597                 usage: "transfer_dst",
2598             });
2599         }
2600 
2601         // VUID-VkBlitImageInfo2-srcImage-01999
2602         if !src_image
2603             .format_features()
2604             .intersects(FormatFeatures::BLIT_SRC)
2605         {
2606             return Err(CopyError::MissingFormatFeature {
2607                 resource: CopyErrorResource::Source,
2608                 format_feature: "blit_src",
2609             });
2610         }
2611 
2612         // VUID-VkBlitImageInfo2-dstImage-02000
2613         if !dst_image
2614             .format_features()
2615             .intersects(FormatFeatures::BLIT_DST)
2616         {
2617             return Err(CopyError::MissingFormatFeature {
2618                 resource: CopyErrorResource::Destination,
2619                 format_feature: "blit_dst",
2620             });
2621         }
2622 
2623         // VUID-VkBlitImageInfo2-srcImage-06421
2624         if src_image.format().ycbcr_chroma_sampling().is_some() {
2625             return Err(CopyError::FormatNotSupported {
2626                 resource: CopyErrorResource::Source,
2627                 format: src_image.format(),
2628             });
2629         }
2630 
2631         // VUID-VkBlitImageInfo2-dstImage-06422
2632         if dst_image.format().ycbcr_chroma_sampling().is_some() {
2633             return Err(CopyError::FormatNotSupported {
2634                 resource: CopyErrorResource::Destination,
2635                 format: src_image.format(),
2636             });
2637         }
2638 
2639         if !(src_image_aspects.intersects(ImageAspects::COLOR)
2640             && dst_image_aspects.intersects(ImageAspects::COLOR))
2641         {
2642             // VUID-VkBlitImageInfo2-srcImage-00231
2643             if src_image.format() != dst_image.format() {
2644                 return Err(CopyError::FormatsMismatch {
2645                     src_format: src_image.format(),
2646                     dst_format: dst_image.format(),
2647                 });
2648             }
2649         } else {
2650             // VUID-VkBlitImageInfo2-srcImage-00229
2651             // VUID-VkBlitImageInfo2-srcImage-00230
2652             if !matches!(
2653                 (
2654                     src_image.format().type_color().unwrap(),
2655                     dst_image.format().type_color().unwrap()
2656                 ),
2657                 (
2658                     NumericType::SFLOAT
2659                         | NumericType::UFLOAT
2660                         | NumericType::SNORM
2661                         | NumericType::UNORM
2662                         | NumericType::SSCALED
2663                         | NumericType::USCALED
2664                         | NumericType::SRGB,
2665                     NumericType::SFLOAT
2666                         | NumericType::UFLOAT
2667                         | NumericType::SNORM
2668                         | NumericType::UNORM
2669                         | NumericType::SSCALED
2670                         | NumericType::USCALED
2671                         | NumericType::SRGB,
2672                 ) | (NumericType::SINT, NumericType::SINT)
2673                     | (NumericType::UINT, NumericType::UINT)
2674             ) {
2675                 return Err(CopyError::FormatsNotCompatible {
2676                     src_format: src_image.format(),
2677                     dst_format: dst_image.format(),
2678                 });
2679             }
2680         }
2681 
2682         // VUID-VkBlitImageInfo2-srcImage-00233
2683         if src_image.samples() != SampleCount::Sample1 {
2684             return Err(CopyError::SampleCountInvalid {
2685                 resource: CopyErrorResource::Destination,
2686                 sample_count: dst_image.samples(),
2687                 allowed_sample_counts: SampleCounts::SAMPLE_1,
2688             });
2689         }
2690 
2691         // VUID-VkBlitImageInfo2-dstImage-00234
2692         if dst_image.samples() != SampleCount::Sample1 {
2693             return Err(CopyError::SampleCountInvalid {
2694                 resource: CopyErrorResource::Destination,
2695                 sample_count: dst_image.samples(),
2696                 allowed_sample_counts: SampleCounts::SAMPLE_1,
2697             });
2698         }
2699 
2700         // VUID-VkBlitImageInfo2-srcImageLayout-01398
2701         if !matches!(
2702             src_image_layout,
2703             ImageLayout::TransferSrcOptimal | ImageLayout::General
2704         ) {
2705             return Err(CopyError::ImageLayoutInvalid {
2706                 resource: CopyErrorResource::Source,
2707                 image_layout: src_image_layout,
2708             });
2709         }
2710 
2711         // VUID-VkBlitImageInfo2-dstImageLayout-01399
2712         if !matches!(
2713             dst_image_layout,
2714             ImageLayout::TransferDstOptimal | ImageLayout::General
2715         ) {
2716             return Err(CopyError::ImageLayoutInvalid {
2717                 resource: CopyErrorResource::Destination,
2718                 image_layout: dst_image_layout,
2719             });
2720         }
2721 
2722         // VUID-VkBlitImageInfo2-srcImage-00232
2723         if !src_image_aspects.intersects(ImageAspects::COLOR) && filter != Filter::Nearest {
2724             return Err(CopyError::FilterNotSupportedByFormat);
2725         }
2726 
2727         match filter {
2728             Filter::Nearest => (),
2729             Filter::Linear => {
2730                 // VUID-VkBlitImageInfo2-filter-02001
2731                 if !src_image
2732                     .format_features()
2733                     .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR)
2734                 {
2735                     return Err(CopyError::FilterNotSupportedByFormat);
2736                 }
2737             }
2738             Filter::Cubic => {
2739                 // VUID-VkBlitImageInfo2-filter-02002
2740                 if !src_image
2741                     .format_features()
2742                     .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC)
2743                 {
2744                     return Err(CopyError::FilterNotSupportedByFormat);
2745                 }
2746 
2747                 // VUID-VkBlitImageInfo2-filter-00237
2748                 if !matches!(src_image.dimensions(), ImageDimensions::Dim2d { .. }) {
2749                     return Err(CopyError::FilterNotSupportedForImageType);
2750                 }
2751             }
2752         }
2753 
2754         let same_image = src_image_inner.image == dst_image_inner.image;
2755         let mut overlap_subresource_indices = None;
2756         let mut overlap_extent_indices = None;
2757 
2758         for (region_index, region) in regions.iter().enumerate() {
2759             let &ImageBlit {
2760                 ref src_subresource,
2761                 src_offsets,
2762                 ref dst_subresource,
2763                 dst_offsets,
2764                 _ne: _,
2765             } = region;
2766 
2767             let check_subresource = |resource: CopyErrorResource,
2768                                      image: &dyn ImageAccess,
2769                                      image_aspects: ImageAspects,
2770                                      subresource: &ImageSubresourceLayers|
2771              -> Result<_, CopyError> {
2772                 // VUID-VkBlitImageInfo2-srcSubresource-01705
2773                 // VUID-VkBlitImageInfo2-dstSubresource-01706
2774                 if subresource.mip_level >= image.mip_levels() {
2775                     return Err(CopyError::MipLevelsOutOfRange {
2776                         resource,
2777                         region_index,
2778                         mip_levels_range_end: subresource.mip_level + 1,
2779                         image_mip_levels: image.mip_levels(),
2780                     });
2781                 }
2782 
2783                 // VUID-VkImageSubresourceLayers-layerCount-01700
2784                 assert!(!subresource.array_layers.is_empty());
2785 
2786                 // VUID-VkBlitImageInfo2-srcSubresource-01707
2787                 // VUID-VkBlitImageInfo2-dstSubresource-01708
2788                 // VUID-VkBlitImageInfo2-srcImage-00240
2789                 if subresource.array_layers.end > image.dimensions().array_layers() {
2790                     return Err(CopyError::ArrayLayersOutOfRange {
2791                         resource,
2792                         region_index,
2793                         array_layers_range_end: subresource.array_layers.end,
2794                         image_array_layers: image.dimensions().array_layers(),
2795                     });
2796                 }
2797 
2798                 // VUID-VkImageSubresourceLayers-aspectMask-parameter
2799                 subresource.aspects.validate_device(device)?;
2800 
2801                 // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask
2802                 assert!(!subresource.aspects.is_empty());
2803 
2804                 // VUID-VkBlitImageInfo2-aspectMask-00241
2805                 // VUID-VkBlitImageInfo2-aspectMask-00242
2806                 if !image_aspects.contains(subresource.aspects) {
2807                     return Err(CopyError::AspectsNotAllowed {
2808                         resource,
2809                         region_index,
2810                         aspects: subresource.aspects,
2811                         allowed_aspects: image_aspects,
2812                     });
2813                 }
2814 
2815                 Ok(image
2816                     .dimensions()
2817                     .mip_level_dimensions(subresource.mip_level)
2818                     .unwrap()
2819                     .width_height_depth())
2820             };
2821 
2822             let src_subresource_extent = check_subresource(
2823                 CopyErrorResource::Source,
2824                 src_image,
2825                 src_image_aspects,
2826                 src_subresource,
2827             )?;
2828             let dst_subresource_extent = check_subresource(
2829                 CopyErrorResource::Destination,
2830                 dst_image,
2831                 dst_image_aspects,
2832                 dst_subresource,
2833             )?;
2834 
2835             // VUID-VkImageBlit2-aspectMask-00238
2836             if src_subresource.aspects != dst_subresource.aspects {
2837                 return Err(CopyError::AspectsMismatch {
2838                     region_index,
2839                     src_aspects: src_subresource.aspects,
2840                     dst_aspects: dst_subresource.aspects,
2841                 });
2842             }
2843 
2844             let src_layer_count =
2845                 src_subresource.array_layers.end - src_subresource.array_layers.start;
2846             let dst_layer_count =
2847                 dst_subresource.array_layers.end - dst_subresource.array_layers.start;
2848 
2849             // VUID-VkImageBlit2-layerCount-00239
2850             // VUID-VkBlitImageInfo2-srcImage-00240
2851             if src_layer_count != dst_layer_count {
2852                 return Err(CopyError::ArrayLayerCountMismatch {
2853                     region_index,
2854                     src_layer_count,
2855                     dst_layer_count,
2856                 });
2857             }
2858 
2859             let check_offset_extent = |resource: CopyErrorResource,
2860                                        image_type: ImageType,
2861                                        subresource_extent: [u32; 3],
2862                                        offsets: [[u32; 3]; 2]|
2863              -> Result<_, CopyError> {
2864                 match image_type {
2865                     ImageType::Dim1d => {
2866                         // VUID-VkBlitImageInfo2-srcImage-00245
2867                         // VUID-VkBlitImageInfo2-dstImage-00250
2868                         if !(offsets[0][1] == 0 && offsets[1][1] == 1) {
2869                             return Err(CopyError::OffsetsInvalidForImageType {
2870                                 resource,
2871                                 region_index,
2872                                 offsets: [offsets[0][1], offsets[1][1]],
2873                             });
2874                         }
2875 
2876                         // VUID-VkBlitImageInfo2-srcImage-00247
2877                         // VUID-VkBlitImageInfo2-dstImage-00252
2878                         if !(offsets[0][2] == 0 && offsets[1][2] == 1) {
2879                             return Err(CopyError::OffsetsInvalidForImageType {
2880                                 resource,
2881                                 region_index,
2882                                 offsets: [offsets[0][2], offsets[1][2]],
2883                             });
2884                         }
2885                     }
2886                     ImageType::Dim2d => {
2887                         // VUID-VkBlitImageInfo2-srcImage-00247
2888                         // VUID-VkBlitImageInfo2-dstImage-00252
2889                         if !(offsets[0][2] == 0 && offsets[1][2] == 1) {
2890                             return Err(CopyError::OffsetsInvalidForImageType {
2891                                 resource,
2892                                 region_index,
2893                                 offsets: [offsets[0][2], offsets[1][2]],
2894                             });
2895                         }
2896                     }
2897                     ImageType::Dim3d => (),
2898                 }
2899 
2900                 let offset_range_end = [
2901                     max(offsets[0][0], offsets[1][0]),
2902                     max(offsets[0][1], offsets[1][1]),
2903                     max(offsets[0][2], offsets[1][2]),
2904                 ];
2905 
2906                 for i in 0..3 {
2907                     // VUID-VkBlitImageInfo2-srcOffset-00243
2908                     // VUID-VkBlitImageInfo2-srcOffset-00244
2909                     // VUID-VkBlitImageInfo2-srcOffset-00246
2910                     // VUID-VkBlitImageInfo2-dstOffset-00248
2911                     // VUID-VkBlitImageInfo2-dstOffset-00249
2912                     // VUID-VkBlitImageInfo2-dstOffset-00251
2913                     if offset_range_end[i] > subresource_extent[i] {
2914                         return Err(CopyError::RegionOutOfImageBounds {
2915                             resource,
2916                             region_index,
2917                             offset_range_end,
2918                             subresource_extent,
2919                         });
2920                     }
2921                 }
2922 
2923                 Ok(())
2924             };
2925 
2926             check_offset_extent(
2927                 CopyErrorResource::Source,
2928                 src_image_type,
2929                 src_subresource_extent,
2930                 src_offsets,
2931             )?;
2932             check_offset_extent(
2933                 CopyErrorResource::Destination,
2934                 dst_image_type,
2935                 dst_subresource_extent,
2936                 dst_offsets,
2937             )?;
2938 
2939             // VUID-VkBlitImageInfo2-pRegions-00217
2940             if same_image {
2941                 let src_region_index = region_index;
2942                 let src_subresource_axes = [
2943                     src_image_inner.first_mipmap_level + src_subresource.mip_level
2944                         ..src_image_inner.first_mipmap_level + src_subresource.mip_level + 1,
2945                     src_image_inner.first_layer + src_subresource.array_layers.start
2946                         ..src_image_inner.first_layer + src_subresource.array_layers.end,
2947                 ];
2948                 let src_extent_axes = [
2949                     min(src_offsets[0][0], src_offsets[1][0])
2950                         ..max(src_offsets[0][0], src_offsets[1][0]),
2951                     min(src_offsets[0][1], src_offsets[1][1])
2952                         ..max(src_offsets[0][1], src_offsets[1][1]),
2953                     min(src_offsets[0][2], src_offsets[1][2])
2954                         ..max(src_offsets[0][2], src_offsets[1][2]),
2955                 ];
2956 
2957                 for (dst_region_index, dst_region) in regions.iter().enumerate() {
2958                     let &ImageBlit {
2959                         ref dst_subresource,
2960                         dst_offsets,
2961                         ..
2962                     } = dst_region;
2963 
2964                     let dst_subresource_axes = [
2965                         dst_image_inner.first_mipmap_level + dst_subresource.mip_level
2966                             ..dst_image_inner.first_mipmap_level + dst_subresource.mip_level + 1,
2967                         dst_image_inner.first_layer + src_subresource.array_layers.start
2968                             ..dst_image_inner.first_layer + src_subresource.array_layers.end,
2969                     ];
2970 
2971                     if src_subresource_axes.iter().zip(dst_subresource_axes).any(
2972                         |(src_range, dst_range)| {
2973                             src_range.start >= dst_range.end || dst_range.start >= src_range.end
2974                         },
2975                     ) {
2976                         continue;
2977                     }
2978 
2979                     // If the subresource axes all overlap, then the source and destination must
2980                     // have the same layout.
2981                     overlap_subresource_indices = Some((src_region_index, dst_region_index));
2982 
2983                     let dst_extent_axes = [
2984                         min(dst_offsets[0][0], dst_offsets[1][0])
2985                             ..max(dst_offsets[0][0], dst_offsets[1][0]),
2986                         min(dst_offsets[0][1], dst_offsets[1][1])
2987                             ..max(dst_offsets[0][1], dst_offsets[1][1]),
2988                         min(dst_offsets[0][2], dst_offsets[1][2])
2989                             ..max(dst_offsets[0][2], dst_offsets[1][2]),
2990                     ];
2991 
2992                     if src_extent_axes
2993                         .iter()
2994                         .zip(dst_extent_axes)
2995                         .any(|(src_range, dst_range)| {
2996                             src_range.start >= dst_range.end || dst_range.start >= src_range.end
2997                         })
2998                     {
2999                         continue;
3000                     }
3001 
3002                     // If the extent axes *also* overlap, then that's an error.
3003                     overlap_extent_indices = Some((src_region_index, dst_region_index));
3004                 }
3005             }
3006         }
3007 
3008         // VUID-VkBlitImageInfo2-pRegions-00217
3009         if let Some((src_region_index, dst_region_index)) = overlap_extent_indices {
3010             return Err(CopyError::OverlappingRegions {
3011                 src_region_index,
3012                 dst_region_index,
3013             });
3014         }
3015 
3016         // VUID-VkBlitImageInfo2-srcImageLayout-00221
3017         // VUID-VkBlitImageInfo2-dstImageLayout-00226
3018         if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices {
3019             if src_image_layout != dst_image_layout {
3020                 return Err(CopyError::OverlappingSubresourcesLayoutMismatch {
3021                     src_region_index,
3022                     dst_region_index,
3023                     src_image_layout,
3024                     dst_image_layout,
3025                 });
3026             }
3027         }
3028 
3029         // TODO: sync check
3030 
3031         Ok(())
3032     }
3033 
3034     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
blit_image_unchecked(&mut self, blit_image_info: BlitImageInfo) -> &mut Self3035     pub unsafe fn blit_image_unchecked(&mut self, blit_image_info: BlitImageInfo) -> &mut Self {
3036         let BlitImageInfo {
3037             src_image,
3038             src_image_layout,
3039             dst_image,
3040             dst_image_layout,
3041             regions,
3042             filter,
3043             _ne: _,
3044         } = blit_image_info;
3045 
3046         if regions.is_empty() {
3047             return self;
3048         }
3049 
3050         let src_image_inner = src_image.inner();
3051         let dst_image_inner = dst_image.inner();
3052 
3053         let fns = self.device().fns();
3054 
3055         if self.device().api_version() >= Version::V1_3
3056             || self.device().enabled_extensions().khr_copy_commands2
3057         {
3058             let regions: SmallVec<[_; 8]> = regions
3059                 .iter()
3060                 .map(|region| {
3061                     let &ImageBlit {
3062                         ref src_subresource,
3063                         src_offsets,
3064                         ref dst_subresource,
3065                         dst_offsets,
3066                         _ne: _,
3067                     } = region;
3068 
3069                     let mut src_subresource = src_subresource.clone();
3070                     src_subresource.array_layers.start += src_image_inner.first_layer;
3071                     src_subresource.array_layers.end += src_image_inner.first_layer;
3072                     src_subresource.mip_level += src_image_inner.first_mipmap_level;
3073 
3074                     let mut dst_subresource = dst_subresource.clone();
3075                     dst_subresource.array_layers.start += dst_image_inner.first_layer;
3076                     dst_subresource.array_layers.end += dst_image_inner.first_layer;
3077                     dst_subresource.mip_level += dst_image_inner.first_mipmap_level;
3078 
3079                     ash::vk::ImageBlit2 {
3080                         src_subresource: src_subresource.into(),
3081                         src_offsets: [
3082                             ash::vk::Offset3D {
3083                                 x: src_offsets[0][0] as i32,
3084                                 y: src_offsets[0][1] as i32,
3085                                 z: src_offsets[0][2] as i32,
3086                             },
3087                             ash::vk::Offset3D {
3088                                 x: src_offsets[1][0] as i32,
3089                                 y: src_offsets[1][1] as i32,
3090                                 z: src_offsets[1][2] as i32,
3091                             },
3092                         ],
3093                         dst_subresource: dst_subresource.into(),
3094                         dst_offsets: [
3095                             ash::vk::Offset3D {
3096                                 x: dst_offsets[0][0] as i32,
3097                                 y: dst_offsets[0][1] as i32,
3098                                 z: dst_offsets[0][2] as i32,
3099                             },
3100                             ash::vk::Offset3D {
3101                                 x: dst_offsets[1][0] as i32,
3102                                 y: dst_offsets[1][1] as i32,
3103                                 z: dst_offsets[1][2] as i32,
3104                             },
3105                         ],
3106                         ..Default::default()
3107                     }
3108                 })
3109                 .collect();
3110 
3111             let blit_image_info = ash::vk::BlitImageInfo2 {
3112                 src_image: src_image_inner.image.handle(),
3113                 src_image_layout: src_image_layout.into(),
3114                 dst_image: dst_image_inner.image.handle(),
3115                 dst_image_layout: dst_image_layout.into(),
3116                 region_count: regions.len() as u32,
3117                 p_regions: regions.as_ptr(),
3118                 filter: filter.into(),
3119                 ..Default::default()
3120             };
3121 
3122             if self.device().api_version() >= Version::V1_3 {
3123                 (fns.v1_3.cmd_blit_image2)(self.handle(), &blit_image_info);
3124             } else {
3125                 (fns.khr_copy_commands2.cmd_blit_image2_khr)(self.handle(), &blit_image_info);
3126             }
3127         } else {
3128             let regions: SmallVec<[_; 8]> = regions
3129                 .iter()
3130                 .map(|region| {
3131                     let &ImageBlit {
3132                         ref src_subresource,
3133                         src_offsets,
3134                         ref dst_subresource,
3135                         dst_offsets,
3136                         _ne: _,
3137                     } = region;
3138 
3139                     let mut src_subresource = src_subresource.clone();
3140                     src_subresource.array_layers.start += src_image_inner.first_layer;
3141                     src_subresource.array_layers.end += src_image_inner.first_layer;
3142                     src_subresource.mip_level += src_image_inner.first_mipmap_level;
3143 
3144                     let mut dst_subresource = dst_subresource.clone();
3145                     dst_subresource.array_layers.start += dst_image_inner.first_layer;
3146                     dst_subresource.array_layers.end += dst_image_inner.first_layer;
3147                     dst_subresource.mip_level += dst_image_inner.first_mipmap_level;
3148 
3149                     ash::vk::ImageBlit {
3150                         src_subresource: src_subresource.into(),
3151                         src_offsets: [
3152                             ash::vk::Offset3D {
3153                                 x: src_offsets[0][0] as i32,
3154                                 y: src_offsets[0][1] as i32,
3155                                 z: src_offsets[0][2] as i32,
3156                             },
3157                             ash::vk::Offset3D {
3158                                 x: src_offsets[1][0] as i32,
3159                                 y: src_offsets[1][1] as i32,
3160                                 z: src_offsets[1][2] as i32,
3161                             },
3162                         ],
3163                         dst_subresource: dst_subresource.into(),
3164                         dst_offsets: [
3165                             ash::vk::Offset3D {
3166                                 x: dst_offsets[0][0] as i32,
3167                                 y: dst_offsets[0][1] as i32,
3168                                 z: dst_offsets[0][2] as i32,
3169                             },
3170                             ash::vk::Offset3D {
3171                                 x: dst_offsets[1][0] as i32,
3172                                 y: dst_offsets[1][1] as i32,
3173                                 z: dst_offsets[1][2] as i32,
3174                             },
3175                         ],
3176                     }
3177                 })
3178                 .collect();
3179 
3180             (fns.v1_0.cmd_blit_image)(
3181                 self.handle(),
3182                 src_image_inner.image.handle(),
3183                 src_image_layout.into(),
3184                 dst_image_inner.image.handle(),
3185                 dst_image_layout.into(),
3186                 regions.len() as u32,
3187                 regions.as_ptr(),
3188                 filter.into(),
3189             );
3190         }
3191 
3192         let command_index = self.next_command_index;
3193         let command_name = "blit_image";
3194         let src_use_ref = ResourceUseRef {
3195             command_index,
3196             command_name,
3197             resource_in_command: ResourceInCommand::Source,
3198             secondary_use_ref: None,
3199         };
3200         let dst_use_ref = ResourceUseRef {
3201             command_index,
3202             command_name,
3203             resource_in_command: ResourceInCommand::Destination,
3204             secondary_use_ref: None,
3205         };
3206 
3207         for region in regions {
3208             let ImageBlit {
3209                 src_subresource,
3210                 src_offsets: _,
3211                 dst_subresource,
3212                 dst_offsets: _,
3213                 _ne: _,
3214             } = region;
3215 
3216             let mut src_subresource_range = ImageSubresourceRange::from(src_subresource);
3217             src_subresource_range.array_layers.start += src_image_inner.first_layer;
3218             src_subresource_range.array_layers.end += src_image_inner.first_layer;
3219             src_subresource_range.mip_levels.start += src_image_inner.first_mipmap_level;
3220             src_subresource_range.mip_levels.end += src_image_inner.first_mipmap_level;
3221             self.resources_usage_state.record_image_access(
3222                 &src_use_ref,
3223                 src_image_inner.image,
3224                 src_subresource_range,
3225                 PipelineStageAccess::Blit_TransferRead,
3226                 src_image_layout,
3227             );
3228 
3229             let mut dst_subresource_range = ImageSubresourceRange::from(dst_subresource);
3230             dst_subresource_range.array_layers.start += dst_image_inner.first_layer;
3231             dst_subresource_range.array_layers.end += dst_image_inner.first_layer;
3232             dst_subresource_range.mip_levels.start += dst_image_inner.first_mipmap_level;
3233             dst_subresource_range.mip_levels.end += dst_image_inner.first_mipmap_level;
3234             self.resources_usage_state.record_image_access(
3235                 &dst_use_ref,
3236                 dst_image_inner.image,
3237                 dst_subresource_range,
3238                 PipelineStageAccess::Blit_TransferWrite,
3239                 dst_image_layout,
3240             );
3241         }
3242 
3243         self.resources.push(Box::new(src_image));
3244         self.resources.push(Box::new(dst_image));
3245 
3246         self.next_command_index += 1;
3247         self
3248     }
3249 
3250     /// Resolves a multisampled image into a single-sampled image.
3251     ///
3252     /// # Panics
3253     ///
3254     /// - Panics if `src_image` or `dst_image` were not created from the same device
3255     ///   as `self`.
3256     ///
3257     /// # Safety
3258     ///
3259     /// - Appropriate synchronization must be provided for all images
3260     ///   that are accessed by the command.
3261     /// - All images that are accessed by the command must be in the expected image layout.
3262     #[inline]
resolve_image( &mut self, resolve_image_info: ResolveImageInfo, ) -> Result<&mut Self, CopyError>3263     pub unsafe fn resolve_image(
3264         &mut self,
3265         resolve_image_info: ResolveImageInfo,
3266     ) -> Result<&mut Self, CopyError> {
3267         self.validate_resolve_image(&resolve_image_info)?;
3268 
3269         unsafe { Ok(self.resolve_image_unchecked(resolve_image_info)) }
3270     }
3271 
validate_resolve_image( &self, resolve_image_info: &ResolveImageInfo, ) -> Result<(), CopyError>3272     fn validate_resolve_image(
3273         &self,
3274         resolve_image_info: &ResolveImageInfo,
3275     ) -> Result<(), CopyError> {
3276         let device = self.device();
3277 
3278         // VUID-vkCmdResolveImage2-renderpass
3279         if self.builder_state.render_pass.is_some() {
3280             return Err(CopyError::ForbiddenInsideRenderPass);
3281         }
3282 
3283         let queue_family_properties = self.queue_family_properties();
3284 
3285         // VUID-vkCmdResolveImage2-commandBuffer-cmdpool
3286         if !queue_family_properties
3287             .queue_flags
3288             .intersects(QueueFlags::GRAPHICS)
3289         {
3290             return Err(CopyError::NotSupportedByQueueFamily);
3291         }
3292 
3293         let &ResolveImageInfo {
3294             ref src_image,
3295             src_image_layout,
3296             ref dst_image,
3297             dst_image_layout,
3298             ref regions,
3299             _ne: _,
3300         } = resolve_image_info;
3301 
3302         // VUID-VkResolveImageInfo2-srcImageLayout-parameter
3303         src_image_layout.validate_device(device)?;
3304 
3305         // VUID-VkResolveImageInfo2-dstImageLayout-parameter
3306         dst_image_layout.validate_device(device)?;
3307 
3308         // VUID-VkResolveImageInfo2-commonparent
3309         assert_eq!(device, src_image.device());
3310         assert_eq!(device, dst_image.device());
3311 
3312         let src_image_type = src_image.dimensions().image_type();
3313         let dst_image_type = dst_image.dimensions().image_type();
3314 
3315         // VUID-VkResolveImageInfo2-srcImage-00257
3316         if src_image.samples() == SampleCount::Sample1 {
3317             return Err(CopyError::SampleCountInvalid {
3318                 resource: CopyErrorResource::Source,
3319                 sample_count: dst_image.samples(),
3320                 allowed_sample_counts: SampleCounts::SAMPLE_2
3321                     | SampleCounts::SAMPLE_4
3322                     | SampleCounts::SAMPLE_8
3323                     | SampleCounts::SAMPLE_16
3324                     | SampleCounts::SAMPLE_32
3325                     | SampleCounts::SAMPLE_64,
3326             });
3327         }
3328 
3329         // VUID-VkResolveImageInfo2-dstImage-00259
3330         if dst_image.samples() != SampleCount::Sample1 {
3331             return Err(CopyError::SampleCountInvalid {
3332                 resource: CopyErrorResource::Destination,
3333                 sample_count: dst_image.samples(),
3334                 allowed_sample_counts: SampleCounts::SAMPLE_1,
3335             });
3336         }
3337 
3338         // VUID-VkResolveImageInfo2-dstImage-02003
3339         if !dst_image
3340             .format_features()
3341             .intersects(FormatFeatures::COLOR_ATTACHMENT)
3342         {
3343             return Err(CopyError::MissingFormatFeature {
3344                 resource: CopyErrorResource::Destination,
3345                 format_feature: "color_attachment",
3346             });
3347         }
3348 
3349         // VUID-VkResolveImageInfo2-srcImage-01386
3350         if src_image.format() != dst_image.format() {
3351             return Err(CopyError::FormatsMismatch {
3352                 src_format: src_image.format(),
3353                 dst_format: dst_image.format(),
3354             });
3355         }
3356 
3357         // VUID-VkResolveImageInfo2-srcImageLayout-01400
3358         if !matches!(
3359             src_image_layout,
3360             ImageLayout::TransferSrcOptimal | ImageLayout::General
3361         ) {
3362             return Err(CopyError::ImageLayoutInvalid {
3363                 resource: CopyErrorResource::Source,
3364                 image_layout: src_image_layout,
3365             });
3366         }
3367 
3368         // VUID-VkResolveImageInfo2-dstImageLayout-01401
3369         if !matches!(
3370             dst_image_layout,
3371             ImageLayout::TransferDstOptimal | ImageLayout::General
3372         ) {
3373             return Err(CopyError::ImageLayoutInvalid {
3374                 resource: CopyErrorResource::Destination,
3375                 image_layout: dst_image_layout,
3376             });
3377         }
3378 
3379         // Should be guaranteed by the requirement that formats match, and that the destination
3380         // image format features support color attachments.
3381         debug_assert!(
3382             src_image.format().aspects().intersects(ImageAspects::COLOR)
3383                 && dst_image.format().aspects().intersects(ImageAspects::COLOR)
3384         );
3385 
3386         for (region_index, region) in regions.iter().enumerate() {
3387             let &ImageResolve {
3388                 ref src_subresource,
3389                 src_offset,
3390                 ref dst_subresource,
3391                 dst_offset,
3392                 extent,
3393                 _ne: _,
3394             } = region;
3395 
3396             let check_subresource = |resource: CopyErrorResource,
3397                                      image: &dyn ImageAccess,
3398                                      subresource: &ImageSubresourceLayers|
3399              -> Result<_, CopyError> {
3400                 // VUID-VkResolveImageInfo2-srcSubresource-01709
3401                 // VUID-VkResolveImageInfo2-dstSubresource-01710
3402                 if subresource.mip_level >= image.mip_levels() {
3403                     return Err(CopyError::MipLevelsOutOfRange {
3404                         resource,
3405                         region_index,
3406                         mip_levels_range_end: subresource.mip_level + 1,
3407                         image_mip_levels: image.mip_levels(),
3408                     });
3409                 }
3410 
3411                 // VUID-VkImageSubresourceLayers-layerCount-01700
3412                 // VUID-VkResolveImageInfo2-srcImage-04446
3413                 // VUID-VkResolveImageInfo2-srcImage-04447
3414                 assert!(!subresource.array_layers.is_empty());
3415 
3416                 // VUID-VkResolveImageInfo2-srcSubresource-01711
3417                 // VUID-VkResolveImageInfo2-dstSubresource-01712
3418                 // VUID-VkResolveImageInfo2-srcImage-04446
3419                 // VUID-VkResolveImageInfo2-srcImage-04447
3420                 if subresource.array_layers.end > image.dimensions().array_layers() {
3421                     return Err(CopyError::ArrayLayersOutOfRange {
3422                         resource: CopyErrorResource::Destination,
3423                         region_index,
3424                         array_layers_range_end: subresource.array_layers.end,
3425                         image_array_layers: image.dimensions().array_layers(),
3426                     });
3427                 }
3428 
3429                 // VUID-VkImageSubresourceLayers-aspectMask-parameter
3430                 subresource.aspects.validate_device(device)?;
3431 
3432                 // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask
3433                 // VUID-VkImageResolve2-aspectMask-00266
3434                 if subresource.aspects != (ImageAspects::COLOR) {
3435                     return Err(CopyError::AspectsNotAllowed {
3436                         resource,
3437                         region_index,
3438                         aspects: subresource.aspects,
3439                         allowed_aspects: ImageAspects::COLOR,
3440                     });
3441                 }
3442 
3443                 Ok(image
3444                     .dimensions()
3445                     .mip_level_dimensions(subresource.mip_level)
3446                     .unwrap()
3447                     .width_height_depth())
3448             };
3449 
3450             let src_subresource_extent =
3451                 check_subresource(CopyErrorResource::Source, src_image, src_subresource)?;
3452             let dst_subresource_extent =
3453                 check_subresource(CopyErrorResource::Destination, dst_image, dst_subresource)?;
3454 
3455             let src_layer_count =
3456                 src_subresource.array_layers.end - src_subresource.array_layers.start;
3457             let dst_layer_count =
3458                 dst_subresource.array_layers.end - dst_subresource.array_layers.start;
3459 
3460             // VUID-VkImageResolve2-layerCount-00267
3461             // VUID-VkResolveImageInfo2-srcImage-04446
3462             // VUID-VkResolveImageInfo2-srcImage-04447
3463             if src_layer_count != dst_layer_count {
3464                 return Err(CopyError::ArrayLayerCountMismatch {
3465                     region_index,
3466                     src_layer_count,
3467                     dst_layer_count,
3468                 });
3469             }
3470 
3471             // No VUID, but it makes sense?
3472             assert!(extent[0] != 0 && extent[1] != 0 && extent[2] != 0);
3473 
3474             let check_offset_extent = |resource: CopyErrorResource,
3475                                        _image_type: ImageType,
3476                                        subresource_extent: [u32; 3],
3477                                        offset: [u32; 3]|
3478              -> Result<_, CopyError> {
3479                 for i in 0..3 {
3480                     // No VUID, but makes sense?
3481                     assert!(extent[i] != 0);
3482 
3483                     // VUID-VkResolveImageInfo2-srcOffset-00269
3484                     // VUID-VkResolveImageInfo2-srcOffset-00270
3485                     // VUID-VkResolveImageInfo2-srcOffset-00272
3486                     // VUID-VkResolveImageInfo2-dstOffset-00274
3487                     // VUID-VkResolveImageInfo2-dstOffset-00275
3488                     // VUID-VkResolveImageInfo2-dstOffset-00277
3489                     if offset[i] + extent[i] > subresource_extent[i] {
3490                         return Err(CopyError::RegionOutOfImageBounds {
3491                             resource,
3492                             region_index,
3493                             offset_range_end: [
3494                                 offset[0] + extent[0],
3495                                 offset[1] + extent[1],
3496                                 offset[2] + extent[2],
3497                             ],
3498                             subresource_extent,
3499                         });
3500                     }
3501                 }
3502 
3503                 Ok(())
3504             };
3505 
3506             check_offset_extent(
3507                 CopyErrorResource::Source,
3508                 src_image_type,
3509                 src_subresource_extent,
3510                 src_offset,
3511             )?;
3512             check_offset_extent(
3513                 CopyErrorResource::Destination,
3514                 dst_image_type,
3515                 dst_subresource_extent,
3516                 dst_offset,
3517             )?;
3518         }
3519 
3520         // VUID-VkResolveImageInfo2-pRegions-00255
3521         // Can't occur as long as memory aliasing isn't allowed, because `src_image` and
3522         // `dst_image` must have different sample counts and therefore can never be the same image.
3523 
3524         // TODO: sync check
3525 
3526         Ok(())
3527     }
3528 
3529     #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
resolve_image_unchecked( &mut self, resolve_image_info: ResolveImageInfo, ) -> &mut Self3530     pub unsafe fn resolve_image_unchecked(
3531         &mut self,
3532         resolve_image_info: ResolveImageInfo,
3533     ) -> &mut Self {
3534         let ResolveImageInfo {
3535             src_image,
3536             src_image_layout,
3537             dst_image,
3538             dst_image_layout,
3539             regions,
3540             _ne: _,
3541         } = resolve_image_info;
3542 
3543         if regions.is_empty() {
3544             return self;
3545         }
3546 
3547         let src_image_inner = src_image.inner();
3548         let dst_image_inner = dst_image.inner();
3549 
3550         let fns = self.device().fns();
3551 
3552         if self.device().api_version() >= Version::V1_3
3553             || self.device().enabled_extensions().khr_copy_commands2
3554         {
3555             let regions: SmallVec<[_; 8]> = regions
3556                 .iter()
3557                 .map(|region| {
3558                     let &ImageResolve {
3559                         ref src_subresource,
3560                         src_offset,
3561                         ref dst_subresource,
3562                         dst_offset,
3563                         extent,
3564                         _ne: _,
3565                     } = region;
3566 
3567                     let mut src_subresource = src_subresource.clone();
3568                     src_subresource.array_layers.start += src_image_inner.first_layer;
3569                     src_subresource.array_layers.end += src_image_inner.first_layer;
3570                     src_subresource.mip_level += src_image_inner.first_mipmap_level;
3571 
3572                     let mut dst_subresource = dst_subresource.clone();
3573                     dst_subresource.array_layers.start += dst_image_inner.first_layer;
3574                     dst_subresource.array_layers.end += dst_image_inner.first_layer;
3575                     dst_subresource.mip_level += dst_image_inner.first_mipmap_level;
3576 
3577                     ash::vk::ImageResolve2 {
3578                         src_subresource: src_subresource.into(),
3579                         src_offset: ash::vk::Offset3D {
3580                             x: src_offset[0] as i32,
3581                             y: src_offset[1] as i32,
3582                             z: src_offset[2] as i32,
3583                         },
3584                         dst_subresource: dst_subresource.into(),
3585                         dst_offset: ash::vk::Offset3D {
3586                             x: dst_offset[0] as i32,
3587                             y: dst_offset[1] as i32,
3588                             z: dst_offset[2] as i32,
3589                         },
3590                         extent: ash::vk::Extent3D {
3591                             width: extent[0],
3592                             height: extent[1],
3593                             depth: extent[2],
3594                         },
3595                         ..Default::default()
3596                     }
3597                 })
3598                 .collect();
3599 
3600             let resolve_image_info = ash::vk::ResolveImageInfo2 {
3601                 src_image: src_image_inner.image.handle(),
3602                 src_image_layout: src_image_layout.into(),
3603                 dst_image: dst_image_inner.image.handle(),
3604                 dst_image_layout: dst_image_layout.into(),
3605                 region_count: regions.len() as u32,
3606                 p_regions: regions.as_ptr(),
3607                 ..Default::default()
3608             };
3609 
3610             if self.device().api_version() >= Version::V1_3 {
3611                 (fns.v1_3.cmd_resolve_image2)(self.handle(), &resolve_image_info);
3612             } else {
3613                 (fns.khr_copy_commands2.cmd_resolve_image2_khr)(self.handle(), &resolve_image_info);
3614             }
3615         } else {
3616             let regions: SmallVec<[_; 8]> = regions
3617                 .iter()
3618                 .map(|region| {
3619                     let &ImageResolve {
3620                         ref src_subresource,
3621                         src_offset,
3622                         ref dst_subresource,
3623                         dst_offset,
3624                         extent,
3625                         _ne: _,
3626                     } = region;
3627 
3628                     let mut src_subresource = src_subresource.clone();
3629                     src_subresource.array_layers.start += src_image_inner.first_layer;
3630                     src_subresource.array_layers.end += src_image_inner.first_layer;
3631                     src_subresource.mip_level += src_image_inner.first_mipmap_level;
3632 
3633                     let mut dst_subresource = dst_subresource.clone();
3634                     dst_subresource.array_layers.start += dst_image_inner.first_layer;
3635                     dst_subresource.array_layers.end += dst_image_inner.first_layer;
3636                     dst_subresource.mip_level += dst_image_inner.first_mipmap_level;
3637 
3638                     ash::vk::ImageResolve {
3639                         src_subresource: src_subresource.into(),
3640                         src_offset: ash::vk::Offset3D {
3641                             x: src_offset[0] as i32,
3642                             y: src_offset[1] as i32,
3643                             z: src_offset[2] as i32,
3644                         },
3645                         dst_subresource: dst_subresource.into(),
3646                         dst_offset: ash::vk::Offset3D {
3647                             x: dst_offset[0] as i32,
3648                             y: dst_offset[1] as i32,
3649                             z: dst_offset[2] as i32,
3650                         },
3651                         extent: ash::vk::Extent3D {
3652                             width: extent[0],
3653                             height: extent[1],
3654                             depth: extent[2],
3655                         },
3656                     }
3657                 })
3658                 .collect();
3659 
3660             (fns.v1_0.cmd_resolve_image)(
3661                 self.handle(),
3662                 src_image_inner.image.handle(),
3663                 src_image_layout.into(),
3664                 dst_image_inner.image.handle(),
3665                 dst_image_layout.into(),
3666                 regions.len() as u32,
3667                 regions.as_ptr(),
3668             );
3669         }
3670 
3671         let command_index = self.next_command_index;
3672         let command_name = "resolve_image";
3673         let src_use_ref = ResourceUseRef {
3674             command_index,
3675             command_name,
3676             resource_in_command: ResourceInCommand::Source,
3677             secondary_use_ref: None,
3678         };
3679         let dst_use_ref = ResourceUseRef {
3680             command_index,
3681             command_name,
3682             resource_in_command: ResourceInCommand::Destination,
3683             secondary_use_ref: None,
3684         };
3685 
3686         for region in regions {
3687             let ImageResolve {
3688                 src_subresource,
3689                 src_offset: _,
3690                 dst_subresource,
3691                 dst_offset: _,
3692                 extent: _,
3693                 _ne: _,
3694             } = region;
3695 
3696             let mut src_subresource_range = ImageSubresourceRange::from(src_subresource);
3697             src_subresource_range.array_layers.start += src_image_inner.first_layer;
3698             src_subresource_range.array_layers.end += src_image_inner.first_layer;
3699             src_subresource_range.mip_levels.start += src_image_inner.first_mipmap_level;
3700             src_subresource_range.mip_levels.end += src_image_inner.first_mipmap_level;
3701             self.resources_usage_state.record_image_access(
3702                 &src_use_ref,
3703                 src_image_inner.image,
3704                 src_subresource_range,
3705                 PipelineStageAccess::Resolve_TransferRead,
3706                 src_image_layout,
3707             );
3708 
3709             let mut dst_subresource_range = ImageSubresourceRange::from(dst_subresource);
3710             dst_subresource_range.array_layers.start += dst_image_inner.first_layer;
3711             dst_subresource_range.array_layers.end += dst_image_inner.first_layer;
3712             dst_subresource_range.mip_levels.start += dst_image_inner.first_mipmap_level;
3713             dst_subresource_range.mip_levels.end += dst_image_inner.first_mipmap_level;
3714             self.resources_usage_state.record_image_access(
3715                 &dst_use_ref,
3716                 dst_image_inner.image,
3717                 dst_subresource_range,
3718                 PipelineStageAccess::Resolve_TransferWrite,
3719                 dst_image_layout,
3720             );
3721         }
3722 
3723         self.resources.push(Box::new(src_image));
3724         self.resources.push(Box::new(dst_image));
3725 
3726         self.next_command_index += 1;
3727         self
3728     }
3729 }
3730