vulkano/command_buffer/commands/
copy.rs

1use crate::{
2    buffer::{BufferUsage, Subbuffer},
3    command_buffer::{
4        auto::Resource, sys::RecordingCommandBuffer, AutoCommandBufferBuilder, ResourceInCommand,
5    },
6    device::{Device, DeviceOwned, QueueFlags},
7    format::{Format, FormatFeatures},
8    image::{
9        mip_level_extent, sampler::Filter, Image, ImageAspects, ImageLayout,
10        ImageSubresourceLayers, ImageTiling, ImageType, ImageUsage, SampleCount,
11    },
12    sync::PipelineStageAccessFlags,
13    DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject,
14};
15use smallvec::{smallvec, SmallVec};
16use std::{
17    cmp::{max, min},
18    mem::size_of,
19    sync::Arc,
20};
21
22/// # Commands to transfer data between resources.
23impl<L> AutoCommandBufferBuilder<L> {
24    /// Copies data from a buffer to another buffer.
25    ///
26    /// # Panics
27    ///
28    /// - Panics if `src_buffer` or `dst_buffer` were not created from the same device as `self`.
29    pub fn copy_buffer(
30        &mut self,
31        copy_buffer_info: impl Into<CopyBufferInfo>,
32    ) -> Result<&mut Self, Box<ValidationError>> {
33        let copy_buffer_info = copy_buffer_info.into();
34        self.validate_copy_buffer(&copy_buffer_info)?;
35
36        Ok(unsafe { self.copy_buffer_unchecked(copy_buffer_info) })
37    }
38
39    fn validate_copy_buffer(
40        &self,
41        copy_buffer_info: &CopyBufferInfo,
42    ) -> Result<(), Box<ValidationError>> {
43        self.inner.validate_copy_buffer(copy_buffer_info)?;
44
45        if self.builder_state.render_pass.is_some() {
46            return Err(Box::new(ValidationError {
47                problem: "a render pass instance is active".into(),
48                vuids: &["VUID-vkCmdCopyBuffer2-renderpass"],
49                ..Default::default()
50            }));
51        }
52
53        Ok(())
54    }
55
56    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
57    pub unsafe fn copy_buffer_unchecked(
58        &mut self,
59        copy_buffer_info: impl Into<CopyBufferInfo>,
60    ) -> &mut Self {
61        let copy_buffer_info = copy_buffer_info.into();
62        let CopyBufferInfo {
63            src_buffer,
64            dst_buffer,
65            regions,
66            _ne: _,
67        } = &copy_buffer_info;
68
69        self.add_command(
70            "copy_buffer",
71            regions
72                .iter()
73                .flat_map(|region| {
74                    let &BufferCopy {
75                        src_offset,
76                        dst_offset,
77                        size,
78                        _ne: _,
79                    } = region;
80
81                    [
82                        (
83                            ResourceInCommand::Source.into(),
84                            Resource::Buffer {
85                                buffer: src_buffer.clone(),
86                                range: src_offset..src_offset + size,
87                                memory_access: PipelineStageAccessFlags::Copy_TransferRead,
88                            },
89                        ),
90                        (
91                            ResourceInCommand::Destination.into(),
92                            Resource::Buffer {
93                                buffer: dst_buffer.clone(),
94                                range: dst_offset..dst_offset + size,
95                                memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
96                            },
97                        ),
98                    ]
99                })
100                .collect(),
101            move |out: &mut RecordingCommandBuffer| {
102                unsafe { out.copy_buffer_unchecked(&copy_buffer_info) };
103            },
104        );
105
106        self
107    }
108
109    /// Copies data from an image to another image.
110    ///
111    /// There are several restrictions:
112    ///
113    /// - The number of samples in the source and destination images must be equal.
114    /// - The size of the uncompressed element format of the source image must be equal to the
115    ///   compressed element format of the destination.
116    /// - If you copy between depth, stencil or depth-stencil images, the format of both images
117    ///   must match exactly.
118    /// - For two-dimensional images, the Z coordinate must be 0 for the image offsets and 1 for
119    ///   the extent. Same for the Y coordinate for one-dimensional images.
120    /// - For non-array images, the base array layer must be 0 and the number of layers must be 1.
121    ///
122    /// If `layer_count` is greater than 1, the copy will happen between each individual layer as
123    /// if they were separate images.
124    ///
125    /// # Panics
126    ///
127    /// - Panics if `src_image` or `dst_image` were not created from the same device as `self`.
128    pub fn copy_image(
129        &mut self,
130        copy_image_info: CopyImageInfo,
131    ) -> Result<&mut Self, Box<ValidationError>> {
132        self.validate_copy_image(&copy_image_info)?;
133
134        Ok(unsafe { self.copy_image_unchecked(copy_image_info) })
135    }
136
137    fn validate_copy_image(
138        &self,
139        copy_image_info: &CopyImageInfo,
140    ) -> Result<(), Box<ValidationError>> {
141        self.inner.validate_copy_image(copy_image_info)?;
142
143        if self.builder_state.render_pass.is_some() {
144            return Err(Box::new(ValidationError {
145                problem: "a render pass instance is active".into(),
146                vuids: &["VUID-vkCmdCopyImage2-renderpass"],
147                ..Default::default()
148            }));
149        }
150
151        Ok(())
152    }
153
154    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
155    pub unsafe fn copy_image_unchecked(&mut self, copy_image_info: CopyImageInfo) -> &mut Self {
156        let &CopyImageInfo {
157            ref src_image,
158            src_image_layout,
159            ref dst_image,
160            dst_image_layout,
161            ref regions,
162            _ne: _,
163        } = &copy_image_info;
164
165        self.add_command(
166            "copy_image",
167            regions
168                .iter()
169                .flat_map(|region| {
170                    let &ImageCopy {
171                        ref src_subresource,
172                        src_offset: _,
173                        ref dst_subresource,
174                        dst_offset: _,
175                        extent: _,
176                        _ne: _,
177                    } = region;
178
179                    [
180                        (
181                            ResourceInCommand::Source.into(),
182                            Resource::Image {
183                                image: src_image.clone(),
184                                subresource_range: src_subresource.clone().into(),
185                                memory_access: PipelineStageAccessFlags::Copy_TransferRead,
186                                start_layout: src_image_layout,
187                                end_layout: src_image_layout,
188                            },
189                        ),
190                        (
191                            ResourceInCommand::Destination.into(),
192                            Resource::Image {
193                                image: dst_image.clone(),
194                                subresource_range: dst_subresource.clone().into(),
195                                memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
196                                start_layout: dst_image_layout,
197                                end_layout: dst_image_layout,
198                            },
199                        ),
200                    ]
201                })
202                .collect(),
203            move |out: &mut RecordingCommandBuffer| {
204                unsafe { out.copy_image_unchecked(&copy_image_info) };
205            },
206        );
207
208        self
209    }
210
211    /// Copies from a buffer to an image.
212    pub fn copy_buffer_to_image(
213        &mut self,
214        copy_buffer_to_image_info: CopyBufferToImageInfo,
215    ) -> Result<&mut Self, Box<ValidationError>> {
216        self.validate_copy_buffer_to_image(&copy_buffer_to_image_info)?;
217
218        Ok(unsafe { self.copy_buffer_to_image_unchecked(copy_buffer_to_image_info) })
219    }
220
221    fn validate_copy_buffer_to_image(
222        &self,
223        copy_buffer_to_image_info: &CopyBufferToImageInfo,
224    ) -> Result<(), Box<ValidationError>> {
225        self.inner
226            .validate_copy_buffer_to_image(copy_buffer_to_image_info)?;
227
228        if self.builder_state.render_pass.is_some() {
229            return Err(Box::new(ValidationError {
230                problem: "a render pass instance is active".into(),
231                vuids: &["VUID-vkCmdCopyBufferToImage2-renderpass"],
232                ..Default::default()
233            }));
234        }
235
236        Ok(())
237    }
238
239    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
240    pub unsafe fn copy_buffer_to_image_unchecked(
241        &mut self,
242        copy_buffer_to_image_info: CopyBufferToImageInfo,
243    ) -> &mut Self {
244        let &CopyBufferToImageInfo {
245            ref src_buffer,
246            ref dst_image,
247            dst_image_layout,
248            ref regions,
249            _ne: _,
250        } = &copy_buffer_to_image_info;
251
252        self.add_command(
253            "copy_buffer_to_image",
254            regions
255                .iter()
256                .flat_map(|region| {
257                    let &BufferImageCopy {
258                        buffer_offset,
259                        buffer_row_length: _,
260                        buffer_image_height: _,
261                        ref image_subresource,
262                        image_offset: _,
263                        image_extent: _,
264                        _ne: _,
265                    } = region;
266
267                    [
268                        (
269                            ResourceInCommand::Source.into(),
270                            Resource::Buffer {
271                                buffer: src_buffer.clone(),
272                                range: buffer_offset
273                                    ..buffer_offset + region.buffer_copy_size(dst_image.format()),
274                                memory_access: PipelineStageAccessFlags::Copy_TransferRead,
275                            },
276                        ),
277                        (
278                            ResourceInCommand::Destination.into(),
279                            Resource::Image {
280                                image: dst_image.clone(),
281                                subresource_range: image_subresource.clone().into(),
282                                memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
283                                start_layout: dst_image_layout,
284                                end_layout: dst_image_layout,
285                            },
286                        ),
287                    ]
288                })
289                .collect(),
290            move |out: &mut RecordingCommandBuffer| {
291                unsafe { out.copy_buffer_to_image_unchecked(&copy_buffer_to_image_info) };
292            },
293        );
294
295        self
296    }
297
298    /// Copies from an image to a buffer.
299    pub fn copy_image_to_buffer(
300        &mut self,
301        copy_image_to_buffer_info: CopyImageToBufferInfo,
302    ) -> Result<&mut Self, Box<ValidationError>> {
303        self.validate_copy_image_to_buffer(&copy_image_to_buffer_info)?;
304
305        Ok(unsafe { self.copy_image_to_buffer_unchecked(copy_image_to_buffer_info) })
306    }
307
308    fn validate_copy_image_to_buffer(
309        &self,
310        copy_image_to_buffer_info: &CopyImageToBufferInfo,
311    ) -> Result<(), Box<ValidationError>> {
312        self.inner
313            .validate_copy_image_to_buffer(copy_image_to_buffer_info)?;
314
315        if self.builder_state.render_pass.is_some() {
316            return Err(Box::new(ValidationError {
317                problem: "a render pass instance is active".into(),
318                vuids: &["VUID-vkCmdCopyImageToBuffer2-renderpass"],
319                ..Default::default()
320            }));
321        }
322
323        Ok(())
324    }
325
326    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
327    pub unsafe fn copy_image_to_buffer_unchecked(
328        &mut self,
329        copy_image_to_buffer_info: CopyImageToBufferInfo,
330    ) -> &mut Self {
331        let &CopyImageToBufferInfo {
332            ref src_image,
333            src_image_layout,
334            ref dst_buffer,
335            ref regions,
336            _ne: _,
337        } = &copy_image_to_buffer_info;
338
339        self.add_command(
340            "copy_image_to_buffer",
341            regions
342                .iter()
343                .flat_map(|region| {
344                    let &BufferImageCopy {
345                        buffer_offset,
346                        buffer_row_length: _,
347                        buffer_image_height: _,
348                        ref image_subresource,
349                        image_offset: _,
350                        image_extent: _,
351                        _ne: _,
352                    } = region;
353
354                    [
355                        (
356                            ResourceInCommand::Source.into(),
357                            Resource::Image {
358                                image: src_image.clone(),
359                                subresource_range: image_subresource.clone().into(),
360                                memory_access: PipelineStageAccessFlags::Copy_TransferRead,
361                                start_layout: src_image_layout,
362                                end_layout: src_image_layout,
363                            },
364                        ),
365                        (
366                            ResourceInCommand::Destination.into(),
367                            Resource::Buffer {
368                                buffer: dst_buffer.clone(),
369                                range: buffer_offset
370                                    ..buffer_offset + region.buffer_copy_size(src_image.format()),
371                                memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
372                            },
373                        ),
374                    ]
375                })
376                .collect(),
377            move |out: &mut RecordingCommandBuffer| {
378                unsafe { out.copy_image_to_buffer_unchecked(&copy_image_to_buffer_info) };
379            },
380        );
381
382        self
383    }
384
385    /// Blits an image to another.
386    ///
387    /// A *blit* is similar to an image copy operation, except that the portion of the image that
388    /// is transferred can be resized. You choose an area of the source and an area of the
389    /// destination, and the implementation will resize the area of the source so that it matches
390    /// the size of the area of the destination before writing it.
391    ///
392    /// Blit operations have several restrictions:
393    ///
394    /// - Blit operations are only allowed on queue families that support graphics operations.
395    /// - The format of the source and destination images must support blit operations, which
396    ///   depends on the Vulkan implementation. Vulkan guarantees that some specific formats must
397    ///   always be supported. See tables 52 to 61 of the specifications.
398    /// - Only single-sampled images are allowed.
399    /// - You can only blit between two images whose formats belong to the same type. The types
400    ///   are: floating-point, signed integers, unsigned integers, depth-stencil.
401    /// - If you blit between depth, stencil or depth-stencil images, the format of both images
402    ///   must match exactly.
403    /// - If you blit between depth, stencil or depth-stencil images, only the `Nearest` filter is
404    ///   allowed.
405    /// - For two-dimensional images, the Z coordinate must be 0 for the top-left offset and 1 for
406    ///   the bottom-right offset. Same for the Y coordinate for one-dimensional images.
407    /// - For non-array images, the base array layer must be 0 and the number of layers must be 1.
408    ///
409    /// If `layer_count` is greater than 1, the blit will happen between each individual layer as
410    /// if they were separate images.
411    ///
412    /// # Panics
413    ///
414    /// - Panics if the source or the destination was not created with `device`.
415    pub fn blit_image(
416        &mut self,
417        blit_image_info: BlitImageInfo,
418    ) -> Result<&mut Self, Box<ValidationError>> {
419        self.validate_blit_image(&blit_image_info)?;
420
421        Ok(unsafe { self.blit_image_unchecked(blit_image_info) })
422    }
423
424    fn validate_blit_image(
425        &self,
426        blit_image_info: &BlitImageInfo,
427    ) -> Result<(), Box<ValidationError>> {
428        self.inner.validate_blit_image(blit_image_info)?;
429
430        if self.builder_state.render_pass.is_some() {
431            return Err(Box::new(ValidationError {
432                problem: "a render pass instance is active".into(),
433                vuids: &["VUID-vkCmdBlitImage2-renderpass"],
434                ..Default::default()
435            }));
436        }
437
438        Ok(())
439    }
440
441    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
442    pub unsafe fn blit_image_unchecked(&mut self, blit_image_info: BlitImageInfo) -> &mut Self {
443        let &BlitImageInfo {
444            ref src_image,
445            src_image_layout,
446            ref dst_image,
447            dst_image_layout,
448            ref regions,
449            filter: _,
450            _ne: _,
451        } = &blit_image_info;
452
453        self.add_command(
454            "blit_image",
455            regions
456                .iter()
457                .flat_map(|region| {
458                    let &ImageBlit {
459                        ref src_subresource,
460                        src_offsets: _,
461                        ref dst_subresource,
462                        dst_offsets: _,
463                        _ne: _,
464                    } = region;
465
466                    [
467                        (
468                            ResourceInCommand::Source.into(),
469                            Resource::Image {
470                                image: src_image.clone(),
471                                subresource_range: src_subresource.clone().into(),
472                                memory_access: PipelineStageAccessFlags::Blit_TransferRead,
473                                start_layout: src_image_layout,
474                                end_layout: src_image_layout,
475                            },
476                        ),
477                        (
478                            ResourceInCommand::Destination.into(),
479                            Resource::Image {
480                                image: dst_image.clone(),
481                                subresource_range: dst_subresource.clone().into(),
482                                memory_access: PipelineStageAccessFlags::Blit_TransferWrite,
483                                start_layout: dst_image_layout,
484                                end_layout: dst_image_layout,
485                            },
486                        ),
487                    ]
488                })
489                .collect(),
490            move |out: &mut RecordingCommandBuffer| {
491                unsafe { out.blit_image_unchecked(&blit_image_info) };
492            },
493        );
494
495        self
496    }
497
498    /// Resolves a multisampled image into a single-sampled image.
499    ///
500    /// # Panics
501    ///
502    /// - Panics if `src_image` or `dst_image` were not created from the same device as `self`.
503    pub fn resolve_image(
504        &mut self,
505        resolve_image_info: ResolveImageInfo,
506    ) -> Result<&mut Self, Box<ValidationError>> {
507        self.validate_resolve_image(&resolve_image_info)?;
508
509        Ok(unsafe { self.resolve_image_unchecked(resolve_image_info) })
510    }
511
512    fn validate_resolve_image(
513        &self,
514        resolve_image_info: &ResolveImageInfo,
515    ) -> Result<(), Box<ValidationError>> {
516        self.inner.validate_resolve_image(resolve_image_info)?;
517
518        if self.builder_state.render_pass.is_some() {
519            return Err(Box::new(ValidationError {
520                problem: "a render pass instance is active".into(),
521                vuids: &["VUID-vkCmdResolveImage2-renderpass"],
522                ..Default::default()
523            }));
524        }
525
526        Ok(())
527    }
528
529    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
530    pub unsafe fn resolve_image_unchecked(
531        &mut self,
532        resolve_image_info: ResolveImageInfo,
533    ) -> &mut Self {
534        let &ResolveImageInfo {
535            ref src_image,
536            src_image_layout,
537            ref dst_image,
538            dst_image_layout,
539            ref regions,
540            _ne: _,
541        } = &resolve_image_info;
542
543        self.add_command(
544            "resolve_image",
545            regions
546                .iter()
547                .flat_map(|region| {
548                    let &ImageResolve {
549                        ref src_subresource,
550                        src_offset: _,
551                        ref dst_subresource,
552                        dst_offset: _,
553                        extent: _,
554                        _ne: _,
555                    } = region;
556
557                    [
558                        (
559                            ResourceInCommand::Source.into(),
560                            Resource::Image {
561                                image: src_image.clone(),
562                                subresource_range: src_subresource.clone().into(),
563                                memory_access: PipelineStageAccessFlags::Resolve_TransferRead,
564                                start_layout: src_image_layout,
565                                end_layout: src_image_layout,
566                            },
567                        ),
568                        (
569                            ResourceInCommand::Destination.into(),
570                            Resource::Image {
571                                image: dst_image.clone(),
572                                subresource_range: dst_subresource.clone().into(),
573                                memory_access: PipelineStageAccessFlags::Resolve_TransferWrite,
574                                start_layout: dst_image_layout,
575                                end_layout: dst_image_layout,
576                            },
577                        ),
578                    ]
579                })
580                .collect(),
581            move |out: &mut RecordingCommandBuffer| {
582                unsafe { out.resolve_image_unchecked(&resolve_image_info) };
583            },
584        );
585
586        self
587    }
588}
589
590impl RecordingCommandBuffer {
591    #[inline]
592    pub unsafe fn copy_buffer(
593        &mut self,
594        copy_buffer_info: &CopyBufferInfo,
595    ) -> Result<&mut Self, Box<ValidationError>> {
596        self.validate_copy_buffer(copy_buffer_info)?;
597
598        Ok(unsafe { self.copy_buffer_unchecked(copy_buffer_info) })
599    }
600
601    fn validate_copy_buffer(
602        &self,
603        copy_buffer_info: &CopyBufferInfo,
604    ) -> Result<(), Box<ValidationError>> {
605        if !self
606            .queue_family_properties()
607            .queue_flags
608            .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
609        {
610            return Err(Box::new(ValidationError {
611                problem: "the queue family of the command buffer does not support \
612                    transfer, graphics or compute operations"
613                    .into(),
614                vuids: &["VUID-vkCmdCopyBuffer2-commandBuffer-cmdpool"],
615                ..Default::default()
616            }));
617        }
618
619        copy_buffer_info
620            .validate(self.device())
621            .map_err(|err| err.add_context("copy_buffer_info"))?;
622
623        Ok(())
624    }
625
626    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
627    pub unsafe fn copy_buffer_unchecked(&mut self, copy_buffer_info: &CopyBufferInfo) -> &mut Self {
628        if copy_buffer_info.regions.is_empty() {
629            return self;
630        }
631
632        let fns = self.device().fns();
633
634        if self.device().api_version() >= Version::V1_3
635            || self.device().enabled_extensions().khr_copy_commands2
636        {
637            let regions_vk = copy_buffer_info.to_vk2_regions();
638            let copy_buffer_info_vk = copy_buffer_info.to_vk2(&regions_vk);
639
640            if self.device().api_version() >= Version::V1_3 {
641                unsafe { (fns.v1_3.cmd_copy_buffer2)(self.handle(), &copy_buffer_info_vk) };
642            } else {
643                unsafe {
644                    (fns.khr_copy_commands2.cmd_copy_buffer2_khr)(
645                        self.handle(),
646                        &copy_buffer_info_vk,
647                    )
648                };
649            }
650        } else {
651            let regions_vk = copy_buffer_info.to_vk_regions();
652            let CopyBufferInfoVk {
653                src_buffer_vk,
654                dst_buffer_vk,
655            } = copy_buffer_info.to_vk();
656
657            unsafe {
658                (fns.v1_0.cmd_copy_buffer)(
659                    self.handle(),
660                    src_buffer_vk,
661                    dst_buffer_vk,
662                    regions_vk.len() as u32,
663                    regions_vk.as_ptr(),
664                )
665            };
666        }
667
668        self
669    }
670
671    #[inline]
672    pub unsafe fn copy_image(
673        &mut self,
674        copy_image_info: &CopyImageInfo,
675    ) -> Result<&mut Self, Box<ValidationError>> {
676        self.validate_copy_image(copy_image_info)?;
677
678        Ok(unsafe { self.copy_image_unchecked(copy_image_info) })
679    }
680
681    fn validate_copy_image(
682        &self,
683        copy_image_info: &CopyImageInfo,
684    ) -> Result<(), Box<ValidationError>> {
685        let queue_family_properties = self.queue_family_properties();
686
687        if !queue_family_properties
688            .queue_flags
689            .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
690        {
691            return Err(Box::new(ValidationError {
692                problem: "the queue family of the command buffer does not support \
693                    transfer, graphics or compute operations"
694                    .into(),
695                vuids: &["VUID-vkCmdCopyImage2-commandBuffer-cmdpool"],
696                ..Default::default()
697            }));
698        }
699
700        copy_image_info
701            .validate(self.device())
702            .map_err(|err| err.add_context("copy_image_info"))?;
703
704        let &CopyImageInfo {
705            ref src_image,
706            src_image_layout: _,
707            ref dst_image,
708            dst_image_layout: _,
709            ref regions,
710            _ne: _,
711        } = copy_image_info;
712
713        let src_image_format = src_image.format();
714        let src_image_format_subsampled_extent = src_image_format
715            .ycbcr_chroma_sampling()
716            .map_or(src_image.extent(), |s| {
717                s.subsampled_extent(src_image.extent())
718            });
719
720        let dst_image_format = dst_image.format();
721        let dst_image_format_subsampled_extent = dst_image_format
722            .ycbcr_chroma_sampling()
723            .map_or(dst_image.extent(), |s| {
724                s.subsampled_extent(dst_image.extent())
725            });
726
727        let min_image_transfer_granularity =
728            // `[1; 3]` means the granularity is 1x1x1 texel, so we can ignore it.
729            // Only check this if there are values greater than 1.
730            (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
731                // `[0; 3]` means only the whole subresource can be copied.
732                (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
733                    // Spec:
734                    // "The value returned in minImageTransferGranularity has a unit of
735                    // compressed texel blocks for images having a block-compressed format,
736                    // and a unit of texels otherwise.""
737
738                    let src_granularity = if src_image_format.compression().is_some() {
739                        let granularity = queue_family_properties.min_image_transfer_granularity;
740                        let block_extent = src_image_format.block_extent();
741
742                        [
743                            granularity[0] * block_extent[0],
744                            granularity[1] * block_extent[1],
745                            granularity[2] * block_extent[2],
746                        ]
747                    } else {
748                        queue_family_properties.min_image_transfer_granularity
749                    };
750
751                    let dst_granularity = if dst_image_format.compression().is_some() {
752                        let granularity = queue_family_properties.min_image_transfer_granularity;
753                        let block_extent = dst_image_format.block_extent();
754
755                        [
756                            granularity[0] * block_extent[0],
757                            granularity[1] * block_extent[1],
758                            granularity[2] * block_extent[2],
759                        ]
760                    } else {
761                        queue_family_properties.min_image_transfer_granularity
762                    };
763
764                    (src_granularity, dst_granularity)
765                })
766            });
767
768        if min_image_transfer_granularity.is_some() {
769            for (region_index, region) in regions.iter().enumerate() {
770                let &ImageCopy {
771                    ref src_subresource,
772                    src_offset,
773                    ref dst_subresource,
774                    dst_offset,
775                    extent,
776                    _ne: _,
777                } = region;
778
779                if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity {
780                    let mut src_subresource_extent =
781                        mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap();
782
783                    if matches!(
784                        src_subresource.aspects,
785                        ImageAspects::PLANE_1 | ImageAspects::PLANE_2
786                    ) {
787                        src_subresource_extent = src_image_format_subsampled_extent;
788                    }
789
790                    let mut dst_subresource_extent =
791                        mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap();
792
793                    if matches!(
794                        dst_subresource.aspects,
795                        ImageAspects::PLANE_1 | ImageAspects::PLANE_2
796                    ) {
797                        dst_subresource_extent = dst_image_format_subsampled_extent;
798                    }
799
800                    if let Some((src_granularity, dst_granularity)) =
801                        &min_image_transfer_granularity
802                    {
803                        /*
804                           Check src
805                        */
806
807                        for i in 0..3 {
808                            if src_offset[i] % src_granularity[i] != 0 {
809                                return Err(Box::new(ValidationError {
810                                    context: "copy_image_info".into(),
811                                    problem: format!(
812                                        "the `min_image_transfer_granularity` property of the \
813                                    queue family of the command buffer is not `[0; 3]`, but \
814                                    `regions[{}].src_offset[{1}]` is not a multiple of \
815                                    `min_image_transfer_granularity[{1}]` texel blocks",
816                                        region_index, i,
817                                    )
818                                    .into(),
819                                    vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"],
820                                    ..Default::default()
821                                }));
822                            }
823
824                            if src_offset[i] + extent[i] != src_subresource_extent[i]
825                                && extent[i] % src_granularity[i] != 0
826                            {
827                                return Err(Box::new(ValidationError {
828                                    context: "copy_image_info".into(),
829                                    problem: format!(
830                                        "the `min_image_transfer_granularity` property of the \
831                                    queue family of the command buffer is not `[0; 3]`, and \
832                                    `regions[{0}].src_offset[{1}] + regions[{0}].extent[{1}]` \
833                                    is not equal to coordinate {1} of the extent of the \
834                                    subresource of `src_image` selected by \
835                                    `regions[{0}].src_subresource`, but \
836                                    `regions[{}].extent[{1}]` is not a multiple of \
837                                    `min_image_transfer_granularity[{1}]` texel blocks",
838                                        region_index, i,
839                                    )
840                                    .into(),
841                                    vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"],
842                                    ..Default::default()
843                                }));
844                            }
845                        }
846
847                        /*
848                           Check dst
849                        */
850
851                        for i in 0..3 {
852                            if dst_offset[i] % dst_granularity[i] != 0 {
853                                return Err(Box::new(ValidationError {
854                                    context: "copy_image_info".into(),
855                                    problem: format!(
856                                        "the `min_image_transfer_granularity` property of the \
857                                    queue family of the command buffer is not `[0; 3]`, but \
858                                    `regions[{}].dst_offset[{1}]` is not a multiple of \
859                                    `min_image_transfer_granularity[{1}]` texel blocks",
860                                        region_index, i,
861                                    )
862                                    .into(),
863                                    vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"],
864                                    ..Default::default()
865                                }));
866                            }
867
868                            if dst_offset[i] + extent[i] != dst_subresource_extent[i]
869                                && extent[i] % dst_granularity[i] != 0
870                            {
871                                return Err(Box::new(ValidationError {
872                                    context: "copy_image_info".into(),
873                                    problem: format!(
874                                        "the `min_image_transfer_granularity` property of the \
875                                    queue family of the command buffer is not `[0; 3]`, and \
876                                    `regions[{0}].dst_offset[{1}] + regions[{0}].extent[{1}]` \
877                                    is not equal to coordinate {1} of the extent of the \
878                                    subresource of `dst_image` selected by \
879                                    `regions[{0}].dst_subresource`, but \
880                                    `regions[{}].extent[{1}]` is not a multiple of \
881                                    `min_image_transfer_granularity[{1}]` texel blocks",
882                                        region_index, i,
883                                    )
884                                    .into(),
885                                    vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"],
886                                    ..Default::default()
887                                }));
888                            }
889                        }
890                    } else {
891                        /*
892                           Check src
893                        */
894
895                        for i in 0..3 {
896                            if src_offset[i] != 0 {
897                                return Err(Box::new(ValidationError {
898                                    context: "copy_image_info".into(),
899                                    problem: format!(
900                                        "the `min_image_transfer_granularity` property of the \
901                                    queue family of the command buffer is `[0; 3]`, but \
902                                    `regions[{}].src_offset[{}]` is not 0",
903                                        region_index, i,
904                                    )
905                                    .into(),
906                                    vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"],
907                                    ..Default::default()
908                                }));
909                            }
910
911                            if src_offset[i] + extent[i] != src_subresource_extent[i] {
912                                return Err(Box::new(ValidationError {
913                                    context: "copy_image_info".into(),
914                                    problem: format!(
915                                        "the `min_image_transfer_granularity` property of the \
916                                    queue family of the command buffer is `[0; 3]`, but \
917                                    `regions[{0}].src_offset[{1}] + regions[{0}].extent[{1}]` \
918                                    is not equal to coordinate {1} of the extent of the \
919                                    subresource of `src_image` selected by \
920                                    `regions[{0}].src_subresource`",
921                                        region_index, i,
922                                    )
923                                    .into(),
924                                    vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"],
925                                    ..Default::default()
926                                }));
927                            }
928                        }
929
930                        /*
931                           Check dst
932                        */
933
934                        for i in 0..3 {
935                            if dst_offset[i] != 0 {
936                                return Err(Box::new(ValidationError {
937                                    context: "copy_image_info".into(),
938                                    problem: format!(
939                                        "the `min_image_transfer_granularity` property of the \
940                                    queue family of the command buffer is `[0; 3]`, but \
941                                    `regions[{}].dst_offset[{}]` is not 0",
942                                        region_index, i,
943                                    )
944                                    .into(),
945                                    vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"],
946                                    ..Default::default()
947                                }));
948                            }
949
950                            if dst_offset[i] + extent[i] != dst_subresource_extent[i] {
951                                return Err(Box::new(ValidationError {
952                                    context: "copy_image_info".into(),
953                                    problem: format!(
954                                        "the `min_image_transfer_granularity` property of the \
955                                    queue family of the command buffer is `[0; 3]`, but \
956                                    `regions[{0}].dst_offset[{1}] + regions[{0}].extent[{1}]` \
957                                    is not equal to coordinate {1} of the extent of the \
958                                    subresource of `dst_image` selected by \
959                                    `regions[{0}].dst_subresource`",
960                                        region_index, i,
961                                    )
962                                    .into(),
963                                    vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"],
964                                    ..Default::default()
965                                }));
966                            }
967                        }
968                    }
969                }
970            }
971        }
972
973        Ok(())
974    }
975
976    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
977    pub unsafe fn copy_image_unchecked(&mut self, copy_image_info: &CopyImageInfo) -> &mut Self {
978        if copy_image_info.regions.is_empty() {
979            return self;
980        }
981
982        let fns = self.device().fns();
983
984        if self.device().api_version() >= Version::V1_3
985            || self.device().enabled_extensions().khr_copy_commands2
986        {
987            let regions_vk = copy_image_info.to_vk2_regions();
988            let copy_image_info_vk = copy_image_info.to_vk2(&regions_vk);
989
990            if self.device().api_version() >= Version::V1_3 {
991                unsafe { (fns.v1_3.cmd_copy_image2)(self.handle(), &copy_image_info_vk) };
992            } else {
993                unsafe {
994                    (fns.khr_copy_commands2.cmd_copy_image2_khr)(self.handle(), &copy_image_info_vk)
995                };
996            }
997        } else {
998            let regions_vk = copy_image_info.to_vk_regions();
999            let CopyImageInfoVk {
1000                src_image_vk,
1001                src_image_layout_vk,
1002                dst_image_vk,
1003                dst_image_layout_vk,
1004            } = copy_image_info.to_vk();
1005
1006            unsafe {
1007                (fns.v1_0.cmd_copy_image)(
1008                    self.handle(),
1009                    src_image_vk,
1010                    src_image_layout_vk,
1011                    dst_image_vk,
1012                    dst_image_layout_vk,
1013                    regions_vk.len() as u32,
1014                    regions_vk.as_ptr(),
1015                )
1016            };
1017        }
1018
1019        self
1020    }
1021
1022    #[inline]
1023    pub unsafe fn copy_buffer_to_image(
1024        &mut self,
1025        copy_buffer_to_image_info: &CopyBufferToImageInfo,
1026    ) -> Result<&mut Self, Box<ValidationError>> {
1027        self.validate_copy_buffer_to_image(copy_buffer_to_image_info)?;
1028
1029        Ok(unsafe { self.copy_buffer_to_image_unchecked(copy_buffer_to_image_info) })
1030    }
1031
1032    fn validate_copy_buffer_to_image(
1033        &self,
1034        copy_buffer_to_image_info: &CopyBufferToImageInfo,
1035    ) -> Result<(), Box<ValidationError>> {
1036        let queue_family_properties = self.queue_family_properties();
1037
1038        if !queue_family_properties
1039            .queue_flags
1040            .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
1041        {
1042            return Err(Box::new(ValidationError {
1043                problem: "the queue family of the command buffer does not support \
1044                    transfer, graphics or compute operations"
1045                    .into(),
1046                vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-cmdpool"],
1047                ..Default::default()
1048            }));
1049        }
1050
1051        copy_buffer_to_image_info
1052            .validate(self.device())
1053            .map_err(|err| err.add_context("copy_buffer_to_image_info"))?;
1054
1055        let &CopyBufferToImageInfo {
1056            src_buffer: _,
1057            ref dst_image,
1058            dst_image_layout: _,
1059            ref regions,
1060            _ne,
1061        } = copy_buffer_to_image_info;
1062
1063        let dst_image_format = dst_image.format();
1064        let dst_image_format_subsampled_extent = dst_image_format
1065            .ycbcr_chroma_sampling()
1066            .map_or(dst_image.extent(), |s| {
1067                s.subsampled_extent(dst_image.extent())
1068            });
1069
1070        let min_image_transfer_granularity =
1071            // `[1; 3]` means the granularity is 1x1x1 texel, so we can ignore it.
1072            // Only check this if there are values greater than 1.
1073            (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
1074                // `[0; 3]` means only the whole subresource can be copied.
1075                (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
1076                    // Spec:
1077                    // "The value returned in minImageTransferGranularity has a unit of
1078                    // compressed texel blocks for images having a block-compressed format,
1079                    // and a unit of texels otherwise.""
1080
1081                    if dst_image_format.compression().is_some() {
1082                        let granularity = queue_family_properties.min_image_transfer_granularity;
1083                        let block_extent = dst_image_format.block_extent();
1084
1085                        [
1086                            granularity[0] * block_extent[0],
1087                            granularity[1] * block_extent[1],
1088                            granularity[2] * block_extent[2],
1089                        ]
1090                    } else {
1091                        queue_family_properties.min_image_transfer_granularity
1092                    }
1093                })
1094            });
1095
1096        let queue_family_no_graphics = !queue_family_properties
1097            .queue_flags
1098            .intersects(QueueFlags::GRAPHICS);
1099        let queue_family_no_compute = !queue_family_properties
1100            .queue_flags
1101            .intersects(QueueFlags::COMPUTE);
1102
1103        if min_image_transfer_granularity.is_some() || queue_family_no_graphics {
1104            for (region_index, region) in regions.iter().enumerate() {
1105                let &BufferImageCopy {
1106                    buffer_offset,
1107                    buffer_row_length: _,
1108                    buffer_image_height: _,
1109                    ref image_subresource,
1110                    image_offset,
1111                    image_extent,
1112                    _ne,
1113                } = region;
1114
1115                if queue_family_no_graphics {
1116                    if queue_family_no_compute && buffer_offset % 4 != 0 {
1117                        return Err(Box::new(ValidationError {
1118                            context: "create_info".into(),
1119                            problem: format!(
1120                                "the queue family of the command buffer does not support \
1121                                graphics or compute operations, but \
1122                                `regions[{}].buffer_offset` is not a multiple of 4",
1123                                region_index
1124                            )
1125                            .into(),
1126                            vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-07737"],
1127                            ..Default::default()
1128                        }));
1129                    }
1130
1131                    if image_subresource
1132                        .aspects
1133                        .intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
1134                    {
1135                        return Err(Box::new(ValidationError {
1136                            context: "create_info".into(),
1137                            problem: format!(
1138                                "the queue family of the command buffer does not support \
1139                                graphics operations, but \
1140                                `regions[{}].image_subresource.aspects` contains \
1141                                `ImageAspects::DEPTH` or `ImageAspects::STENCIL`",
1142                                region_index
1143                            )
1144                            .into(),
1145                            vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-07739"],
1146                            ..Default::default()
1147                        }));
1148                    }
1149                }
1150
1151                if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity {
1152                    let mut image_subresource_extent =
1153                        mip_level_extent(dst_image.extent(), image_subresource.mip_level).unwrap();
1154
1155                    if matches!(
1156                        image_subresource.aspects,
1157                        ImageAspects::PLANE_1 | ImageAspects::PLANE_2
1158                    ) {
1159                        image_subresource_extent = dst_image_format_subsampled_extent;
1160                    }
1161
1162                    if let Some(dst_granularity) = &min_image_transfer_granularity {
1163                        for i in 0..3 {
1164                            if image_offset[i] % dst_granularity[i] != 0 {
1165                                return Err(Box::new(ValidationError {
1166                                    context: "copy_image_info".into(),
1167                                    problem: format!(
1168                                        "the `min_image_transfer_granularity` property of the \
1169                                        queue family of the command buffer is not `[0; 3]`, but \
1170                                        `regions[{}].image_offset[{1}]` is not a multiple of \
1171                                        `min_image_transfer_granularity[{1}]` texel blocks",
1172                                        region_index, i,
1173                                    )
1174                                    .into(),
1175                                    vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"],
1176                                    ..Default::default()
1177                                }));
1178                            }
1179
1180                            if image_offset[i] + image_extent[i] != image_subresource_extent[i]
1181                                && image_extent[i] % dst_granularity[i] != 0
1182                            {
1183                                return Err(Box::new(ValidationError {
1184                                    context: "copy_image_info".into(),
1185                                    problem: format!(
1186                                        "the `min_image_transfer_granularity` property of the \
1187                                        queue family of the command buffer is not `[0; 3]`, and \
1188                                        `regions[{0}].image_offset[{1}] + \
1189                                        regions[{0}].image_extent[{1}]` \
1190                                        is not equal to coordinate {1} of the extent of the \
1191                                        subresource of `dst_image` selected by \
1192                                        `regions[{0}].image_subresource`, but \
1193                                        `regions[{}].image_extent[{1}]` is not a multiple of \
1194                                        `min_image_transfer_granularity[{1}]` texel blocks",
1195                                        region_index, i,
1196                                    )
1197                                    .into(),
1198                                    vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"],
1199                                    ..Default::default()
1200                                }));
1201                            }
1202                        }
1203                    } else {
1204                        for i in 0..3 {
1205                            if image_offset[i] != 0 {
1206                                return Err(Box::new(ValidationError {
1207                                    context: "copy_image_info".into(),
1208                                    problem: format!(
1209                                        "the `min_image_transfer_granularity` property of the \
1210                                        queue family of the command buffer is `[0; 3]`, but \
1211                                        `regions[{}].image_offset[{}]` is not 0",
1212                                        region_index, i,
1213                                    )
1214                                    .into(),
1215                                    vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"],
1216                                    ..Default::default()
1217                                }));
1218                            }
1219
1220                            if image_offset[i] + image_extent[i] != image_subresource_extent[i] {
1221                                return Err(Box::new(ValidationError {
1222                                    context: "copy_image_info".into(),
1223                                    problem: format!(
1224                                        "the `min_image_transfer_granularity` property of the \
1225                                        queue family of the command buffer is `[0; 3]`, but \
1226                                        `regions[{0}].image_offset[{1}] + \
1227                                        regions[{0}].image_extent[{1}]` \
1228                                        is not equal to coordinate {1} of the extent of the \
1229                                        subresource of `dst_image` selected by \
1230                                        `regions[{0}].image_subresource`",
1231                                        region_index, i,
1232                                    )
1233                                    .into(),
1234                                    vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"],
1235                                    ..Default::default()
1236                                }));
1237                            }
1238                        }
1239                    }
1240                }
1241            }
1242        }
1243
1244        Ok(())
1245    }
1246
1247    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1248    pub unsafe fn copy_buffer_to_image_unchecked(
1249        &mut self,
1250        copy_buffer_to_image_info: &CopyBufferToImageInfo,
1251    ) -> &mut Self {
1252        if copy_buffer_to_image_info.regions.is_empty() {
1253            return self;
1254        }
1255
1256        let fns = self.device().fns();
1257
1258        if self.device().api_version() >= Version::V1_3
1259            || self.device().enabled_extensions().khr_copy_commands2
1260        {
1261            let regions_vk = copy_buffer_to_image_info.to_vk2_regions();
1262            let copy_buffer_to_image_info_vk = copy_buffer_to_image_info.to_vk2(&regions_vk);
1263
1264            if self.device().api_version() >= Version::V1_3 {
1265                unsafe {
1266                    (fns.v1_3.cmd_copy_buffer_to_image2)(
1267                        self.handle(),
1268                        &copy_buffer_to_image_info_vk,
1269                    )
1270                };
1271            } else {
1272                unsafe {
1273                    (fns.khr_copy_commands2.cmd_copy_buffer_to_image2_khr)(
1274                        self.handle(),
1275                        &copy_buffer_to_image_info_vk,
1276                    )
1277                };
1278            }
1279        } else {
1280            let regions_vk = copy_buffer_to_image_info.to_vk_regions();
1281            let CopyBufferToImageInfoVk {
1282                src_buffer_vk,
1283                dst_image_vk,
1284                dst_image_layout_vk,
1285            } = copy_buffer_to_image_info.to_vk();
1286
1287            unsafe {
1288                (fns.v1_0.cmd_copy_buffer_to_image)(
1289                    self.handle(),
1290                    src_buffer_vk,
1291                    dst_image_vk,
1292                    dst_image_layout_vk,
1293                    regions_vk.len() as u32,
1294                    regions_vk.as_ptr(),
1295                )
1296            };
1297        }
1298
1299        self
1300    }
1301
1302    #[inline]
1303    pub unsafe fn copy_image_to_buffer(
1304        &mut self,
1305        copy_image_to_buffer_info: &CopyImageToBufferInfo,
1306    ) -> Result<&mut Self, Box<ValidationError>> {
1307        self.validate_copy_image_to_buffer(copy_image_to_buffer_info)?;
1308
1309        Ok(unsafe { self.copy_image_to_buffer_unchecked(copy_image_to_buffer_info) })
1310    }
1311
1312    fn validate_copy_image_to_buffer(
1313        &self,
1314        copy_image_to_buffer_info: &CopyImageToBufferInfo,
1315    ) -> Result<(), Box<ValidationError>> {
1316        let queue_family_properties = self.queue_family_properties();
1317
1318        if !queue_family_properties
1319            .queue_flags
1320            .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
1321        {
1322            return Err(Box::new(ValidationError {
1323                problem: "the queue family of the command buffer does not support \
1324                    transfer, graphics or compute operations"
1325                    .into(),
1326                vuids: &["VUID-vkCmdCopyImageToBuffer2-commandBuffer-cmdpool"],
1327                ..Default::default()
1328            }));
1329        }
1330
1331        copy_image_to_buffer_info
1332            .validate(self.device())
1333            .map_err(|err| err.add_context("copy_image_to_buffer_info"))?;
1334
1335        let &CopyImageToBufferInfo {
1336            ref src_image,
1337            src_image_layout: _,
1338            dst_buffer: _,
1339            ref regions,
1340            _ne,
1341        } = copy_image_to_buffer_info;
1342
1343        let src_image_format = src_image.format();
1344        let src_image_format_subsampled_extent = src_image_format
1345            .ycbcr_chroma_sampling()
1346            .map_or(src_image.extent(), |s| {
1347                s.subsampled_extent(src_image.extent())
1348            });
1349
1350        let min_image_transfer_granularity =
1351            // `[1; 3]` means the granularity is 1x1x1 texel, so we can ignore it.
1352            // Only check this if there are values greater than 1.
1353            (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
1354                // `[0; 3]` means only the whole subresource can be copied.
1355                (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
1356                    // Spec:
1357                    // "The value returned in minImageTransferGranularity has a unit of
1358                    // compressed texel blocks for images having a block-compressed format,
1359                    // and a unit of texels otherwise.""
1360
1361                    if src_image_format.compression().is_some() {
1362                        let granularity = queue_family_properties.min_image_transfer_granularity;
1363                        let block_extent = src_image_format.block_extent();
1364
1365                        [
1366                            granularity[0] * block_extent[0],
1367                            granularity[1] * block_extent[1],
1368                            granularity[2] * block_extent[2],
1369                        ]
1370                    } else {
1371                        queue_family_properties.min_image_transfer_granularity
1372                    }
1373                })
1374            });
1375
1376        let queue_family_no_graphics = !queue_family_properties
1377            .queue_flags
1378            .intersects(QueueFlags::GRAPHICS);
1379        let queue_family_no_compute = !queue_family_properties
1380            .queue_flags
1381            .intersects(QueueFlags::COMPUTE);
1382
1383        if min_image_transfer_granularity.is_some() || queue_family_no_graphics {
1384            for (region_index, region) in regions.iter().enumerate() {
1385                let &BufferImageCopy {
1386                    buffer_offset,
1387                    buffer_row_length: _,
1388                    buffer_image_height: _,
1389                    ref image_subresource,
1390                    image_offset,
1391                    image_extent,
1392                    _ne,
1393                } = region;
1394
1395                if queue_family_no_graphics && queue_family_no_compute && buffer_offset % 4 != 0 {
1396                    return Err(Box::new(ValidationError {
1397                        context: "create_info".into(),
1398                        problem: format!(
1399                            "the queue family of the command buffer does not support \
1400                                graphics or compute operations, but \
1401                                `regions[{}].buffer_offset` is not a multiple of 4",
1402                            region_index
1403                        )
1404                        .into(),
1405                        vuids: &["VUID-vkCmdCopyImageToBuffer2-commandBuffer-07746"],
1406                        ..Default::default()
1407                    }));
1408                }
1409
1410                if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity {
1411                    let mut image_subresource_extent =
1412                        mip_level_extent(src_image.extent(), image_subresource.mip_level).unwrap();
1413
1414                    if matches!(
1415                        image_subresource.aspects,
1416                        ImageAspects::PLANE_1 | ImageAspects::PLANE_2
1417                    ) {
1418                        image_subresource_extent = src_image_format_subsampled_extent;
1419                    }
1420
1421                    if let Some(src_granularity) = &min_image_transfer_granularity {
1422                        for i in 0..3 {
1423                            if image_offset[i] % src_granularity[i] != 0 {
1424                                return Err(Box::new(ValidationError {
1425                                    context: "copy_image_info".into(),
1426                                    problem: format!(
1427                                        "the `min_image_transfer_granularity` property of the \
1428                                        queue family of the command buffer is not `[0; 3]`, but \
1429                                        `regions[{}].image_offset[{1}]` is not a multiple of \
1430                                        `min_image_transfer_granularity[{1}]` texel blocks",
1431                                        region_index, i,
1432                                    )
1433                                    .into(),
1434                                    vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"],
1435                                    ..Default::default()
1436                                }));
1437                            }
1438
1439                            if image_offset[i] + image_extent[i] != image_subresource_extent[i]
1440                                && image_extent[i] % src_granularity[i] != 0
1441                            {
1442                                return Err(Box::new(ValidationError {
1443                                    context: "copy_image_info".into(),
1444                                    problem: format!(
1445                                        "the `min_image_transfer_granularity` property of the \
1446                                        queue family of the command buffer is not `[0; 3]`, and \
1447                                        `regions[{0}].image_offset[{1}] + \
1448                                        regions[{0}].image_extent[{1}]` \
1449                                        is not equal to coordinate {1} of the extent of the \
1450                                        subresource of `src_image` selected by \
1451                                        `regions[{0}].image_subresource`, but \
1452                                        `regions[{}].image_extent[{1}]` is not a multiple of \
1453                                        `min_image_transfer_granularity[{1}]` texel blocks",
1454                                        region_index, i,
1455                                    )
1456                                    .into(),
1457                                    vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"],
1458                                    ..Default::default()
1459                                }));
1460                            }
1461                        }
1462                    } else {
1463                        for i in 0..3 {
1464                            if image_offset[i] != 0 {
1465                                return Err(Box::new(ValidationError {
1466                                    context: "copy_image_info".into(),
1467                                    problem: format!(
1468                                        "the `min_image_transfer_granularity` property of the \
1469                                        queue family of the command buffer is `[0; 3]`, but \
1470                                        `regions[{}].image_offset[{}]` is not 0",
1471                                        region_index, i,
1472                                    )
1473                                    .into(),
1474                                    vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"],
1475                                    ..Default::default()
1476                                }));
1477                            }
1478
1479                            if image_offset[i] + image_extent[i] != image_subresource_extent[i] {
1480                                return Err(Box::new(ValidationError {
1481                                    context: "copy_image_info".into(),
1482                                    problem: format!(
1483                                        "the `min_image_transfer_granularity` property of the \
1484                                        queue family of the command buffer is `[0; 3]`, but \
1485                                        `regions[{0}].image_offset[{1}] + \
1486                                        regions[{0}].image_extent[{1}]` \
1487                                        is not equal to coordinate {1} of the extent of the \
1488                                        subresource of `src_image` selected by \
1489                                        `regions[{0}].image_subresource`",
1490                                        region_index, i,
1491                                    )
1492                                    .into(),
1493                                    vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"],
1494                                    ..Default::default()
1495                                }));
1496                            }
1497                        }
1498                    }
1499                }
1500            }
1501        }
1502
1503        Ok(())
1504    }
1505
1506    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1507    pub unsafe fn copy_image_to_buffer_unchecked(
1508        &mut self,
1509        copy_image_to_buffer_info: &CopyImageToBufferInfo,
1510    ) -> &mut Self {
1511        if copy_image_to_buffer_info.regions.is_empty() {
1512            return self;
1513        }
1514
1515        let fns = self.device().fns();
1516
1517        if self.device().api_version() >= Version::V1_3
1518            || self.device().enabled_extensions().khr_copy_commands2
1519        {
1520            let regions_vk = copy_image_to_buffer_info.to_vk2_regions();
1521            let copy_image_to_buffer_info_vk = copy_image_to_buffer_info.to_vk2(&regions_vk);
1522
1523            if self.device().api_version() >= Version::V1_3 {
1524                unsafe {
1525                    (fns.v1_3.cmd_copy_image_to_buffer2)(
1526                        self.handle(),
1527                        &copy_image_to_buffer_info_vk,
1528                    )
1529                };
1530            } else {
1531                unsafe {
1532                    (fns.khr_copy_commands2.cmd_copy_image_to_buffer2_khr)(
1533                        self.handle(),
1534                        &copy_image_to_buffer_info_vk,
1535                    )
1536                };
1537            }
1538        } else {
1539            let regions_vk = copy_image_to_buffer_info.to_vk_regions();
1540            let CopyImageToBufferInfoVk {
1541                src_image_vk,
1542                src_image_layout_vk,
1543                dst_buffer_vk,
1544            } = copy_image_to_buffer_info.to_vk();
1545
1546            unsafe {
1547                (fns.v1_0.cmd_copy_image_to_buffer)(
1548                    self.handle(),
1549                    src_image_vk,
1550                    src_image_layout_vk,
1551                    dst_buffer_vk,
1552                    regions_vk.len() as u32,
1553                    regions_vk.as_ptr(),
1554                )
1555            };
1556        }
1557
1558        self
1559    }
1560
1561    #[inline]
1562    pub unsafe fn blit_image(
1563        &mut self,
1564        blit_image_info: &BlitImageInfo,
1565    ) -> Result<&mut Self, Box<ValidationError>> {
1566        self.validate_blit_image(blit_image_info)?;
1567
1568        Ok(unsafe { self.blit_image_unchecked(blit_image_info) })
1569    }
1570
1571    fn validate_blit_image(
1572        &self,
1573        blit_image_info: &BlitImageInfo,
1574    ) -> Result<(), Box<ValidationError>> {
1575        if !self
1576            .queue_family_properties()
1577            .queue_flags
1578            .intersects(QueueFlags::GRAPHICS)
1579        {
1580            return Err(Box::new(ValidationError {
1581                problem: "the queue family of the command buffer does not support \
1582                     graphics operations"
1583                    .into(),
1584                vuids: &["VUID-vkCmdBlitImage2-commandBuffer-cmdpool"],
1585                ..Default::default()
1586            }));
1587        }
1588
1589        blit_image_info
1590            .validate(self.device())
1591            .map_err(|err| err.add_context("blit_image_info"))?;
1592
1593        Ok(())
1594    }
1595
1596    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1597    pub unsafe fn blit_image_unchecked(&mut self, blit_image_info: &BlitImageInfo) -> &mut Self {
1598        if blit_image_info.regions.is_empty() {
1599            return self;
1600        }
1601
1602        let fns = self.device().fns();
1603
1604        if self.device().api_version() >= Version::V1_3
1605            || self.device().enabled_extensions().khr_copy_commands2
1606        {
1607            let regions_vk = blit_image_info.to_vk2_regions();
1608            let blit_image_info_vk = blit_image_info.to_vk2(&regions_vk);
1609
1610            if self.device().api_version() >= Version::V1_3 {
1611                unsafe { (fns.v1_3.cmd_blit_image2)(self.handle(), &blit_image_info_vk) };
1612            } else {
1613                unsafe {
1614                    (fns.khr_copy_commands2.cmd_blit_image2_khr)(self.handle(), &blit_image_info_vk)
1615                };
1616            }
1617        } else {
1618            let regions_vk = blit_image_info.to_vk_regions();
1619            let BlitImageInfoVk {
1620                src_image_vk,
1621                src_image_layout_vk,
1622                dst_image_vk,
1623                dst_image_layout_vk,
1624                filter_vk,
1625            } = blit_image_info.to_vk();
1626
1627            unsafe {
1628                (fns.v1_0.cmd_blit_image)(
1629                    self.handle(),
1630                    src_image_vk,
1631                    src_image_layout_vk,
1632                    dst_image_vk,
1633                    dst_image_layout_vk,
1634                    regions_vk.len() as u32,
1635                    regions_vk.as_ptr(),
1636                    filter_vk,
1637                )
1638            };
1639        }
1640
1641        self
1642    }
1643
1644    #[inline]
1645    pub unsafe fn resolve_image(
1646        &mut self,
1647        resolve_image_info: &ResolveImageInfo,
1648    ) -> Result<&mut Self, Box<ValidationError>> {
1649        self.validate_resolve_image(resolve_image_info)?;
1650
1651        Ok(unsafe { self.resolve_image_unchecked(resolve_image_info) })
1652    }
1653
1654    fn validate_resolve_image(
1655        &self,
1656        resolve_image_info: &ResolveImageInfo,
1657    ) -> Result<(), Box<ValidationError>> {
1658        if !self
1659            .queue_family_properties()
1660            .queue_flags
1661            .intersects(QueueFlags::GRAPHICS)
1662        {
1663            return Err(Box::new(ValidationError {
1664                problem: "the queue family of the command buffer does not support \
1665                     graphics operations"
1666                    .into(),
1667                vuids: &["VUID-vkCmdResolveImage2-commandBuffer-cmdpool"],
1668                ..Default::default()
1669            }));
1670        }
1671
1672        resolve_image_info
1673            .validate(self.device())
1674            .map_err(|err| err.add_context("resolve_image_info"))?;
1675
1676        Ok(())
1677    }
1678
1679    #[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
1680    pub unsafe fn resolve_image_unchecked(
1681        &mut self,
1682        resolve_image_info: &ResolveImageInfo,
1683    ) -> &mut Self {
1684        if resolve_image_info.regions.is_empty() {
1685            return self;
1686        }
1687
1688        let fns = self.device().fns();
1689
1690        if self.device().api_version() >= Version::V1_3
1691            || self.device().enabled_extensions().khr_copy_commands2
1692        {
1693            let regions_vk = resolve_image_info.to_vk2_regions();
1694            let resolve_image_info_vk = resolve_image_info.to_vk2(&regions_vk);
1695
1696            if self.device().api_version() >= Version::V1_3 {
1697                unsafe { (fns.v1_3.cmd_resolve_image2)(self.handle(), &resolve_image_info_vk) };
1698            } else {
1699                unsafe {
1700                    (fns.khr_copy_commands2.cmd_resolve_image2_khr)(
1701                        self.handle(),
1702                        &resolve_image_info_vk,
1703                    )
1704                };
1705            }
1706        } else {
1707            let regions_vk = resolve_image_info.to_vk_regions();
1708            let ResolveImageInfoVk {
1709                src_image_vk,
1710                src_image_layout_vk,
1711                dst_image_vk,
1712                dst_image_layout_vk,
1713            } = resolve_image_info.to_vk();
1714
1715            unsafe {
1716                (fns.v1_0.cmd_resolve_image)(
1717                    self.handle(),
1718                    src_image_vk,
1719                    src_image_layout_vk,
1720                    dst_image_vk,
1721                    dst_image_layout_vk,
1722                    regions_vk.len() as u32,
1723                    regions_vk.as_ptr(),
1724                )
1725            };
1726        }
1727
1728        self
1729    }
1730}
1731
1732/// Parameters to copy data from a buffer to another buffer.
1733///
1734/// The fields of `regions` represent bytes.
1735#[derive(Clone, Debug)]
1736pub struct CopyBufferInfo {
1737    /// The buffer to copy from.
1738    ///
1739    /// There is no default value.
1740    pub src_buffer: Subbuffer<[u8]>,
1741
1742    /// The buffer to copy to.
1743    ///
1744    /// There is no default value.
1745    pub dst_buffer: Subbuffer<[u8]>,
1746
1747    /// The regions of both buffers to copy between, specified in bytes.
1748    ///
1749    /// The default value is a single region, with zero offsets and a `size` equal to the smallest
1750    /// of the two buffers.
1751    pub regions: SmallVec<[BufferCopy; 1]>,
1752
1753    pub _ne: crate::NonExhaustive,
1754}
1755
1756impl CopyBufferInfo {
1757    /// Returns a `CopyBufferInfo` with the specified `src_buffer` and `dst_buffer`.
1758    #[inline]
1759    pub fn buffers(src_buffer: Subbuffer<impl ?Sized>, dst_buffer: Subbuffer<impl ?Sized>) -> Self {
1760        let region = BufferCopy {
1761            src_offset: 0,
1762            dst_offset: 0,
1763            size: min(src_buffer.size(), dst_buffer.size()),
1764            ..Default::default()
1765        };
1766
1767        Self {
1768            src_buffer: src_buffer.into_bytes(),
1769            dst_buffer: dst_buffer.into_bytes(),
1770            regions: smallvec![region],
1771            _ne: crate::NonExhaustive(()),
1772        }
1773    }
1774
1775    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
1776        let &Self {
1777            ref src_buffer,
1778            ref dst_buffer,
1779            ref regions,
1780            _ne: _,
1781        } = self;
1782
1783        // VUID-VkCopyBufferInfo2-commonparent
1784        assert_eq!(device, src_buffer.device().as_ref());
1785        assert_eq!(device, dst_buffer.device().as_ref());
1786
1787        if !src_buffer
1788            .buffer()
1789            .usage()
1790            .intersects(BufferUsage::TRANSFER_SRC)
1791        {
1792            return Err(Box::new(ValidationError {
1793                context: "src_buffer.buffer().usage()".into(),
1794                problem: "does not contain `BufferUsage::TRANSFER_SRC`".into(),
1795                vuids: &["VUID-VkCopyBufferInfo2-srcBuffer-00118"],
1796                ..Default::default()
1797            }));
1798        }
1799
1800        if !dst_buffer
1801            .buffer()
1802            .usage()
1803            .intersects(BufferUsage::TRANSFER_DST)
1804        {
1805            return Err(Box::new(ValidationError {
1806                context: "dst_buffer.buffer().usage()".into(),
1807                problem: "does not contain `BufferUsage::TRANSFER_DST`".into(),
1808                vuids: &["VUID-VkCopyBufferInfo2-dstBuffer-00120"],
1809                ..Default::default()
1810            }));
1811        }
1812
1813        let same_buffer = src_buffer.buffer() == dst_buffer.buffer();
1814        let mut overlap_indices = None;
1815
1816        for (region_index, region) in regions.iter().enumerate() {
1817            region
1818                .validate(device)
1819                .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
1820
1821            let &BufferCopy {
1822                src_offset,
1823                dst_offset,
1824                size,
1825                _ne: _,
1826            } = region;
1827
1828            if src_offset + size > src_buffer.size() {
1829                return Err(Box::new(ValidationError {
1830                    problem: format!(
1831                        "`regions[{0}].src_offset + regions[{0}].size` is greater than \
1832                        `src_buffer.size()`",
1833                        region_index
1834                    )
1835                    .into(),
1836                    vuids: &[
1837                        "VUID-VkCopyBufferInfo2-srcOffset-00113",
1838                        "VUID-VkCopyBufferInfo2-size-00115",
1839                    ],
1840                    ..Default::default()
1841                }));
1842            }
1843
1844            if dst_offset + size > dst_buffer.size() {
1845                return Err(Box::new(ValidationError {
1846                    problem: format!(
1847                        "`regions[{0}].dst_offset + regions[{0}].size` is greater than \
1848                        `dst_buffer.size()`",
1849                        region_index
1850                    )
1851                    .into(),
1852                    vuids: &[
1853                        "VUID-VkCopyBufferInfo2-dstOffset-00114",
1854                        "VUID-VkCopyBufferInfo2-size-00116",
1855                    ],
1856                    ..Default::default()
1857                }));
1858            }
1859
1860            // VUID-VkCopyBufferInfo2-pRegions-00117
1861            if same_buffer {
1862                let src_region_index = region_index;
1863                let src_range =
1864                    src_buffer.offset() + src_offset..src_buffer.offset() + src_offset + size;
1865
1866                for (dst_region_index, dst_region) in regions.iter().enumerate() {
1867                    let &BufferCopy { dst_offset, .. } = dst_region;
1868
1869                    let dst_range =
1870                        dst_buffer.offset() + dst_offset..dst_buffer.offset() + dst_offset + size;
1871
1872                    if src_range.start >= dst_range.end || dst_range.start >= src_range.end {
1873                        // The regions do not overlap
1874                        continue;
1875                    }
1876
1877                    overlap_indices = Some((src_region_index, dst_region_index));
1878                }
1879            }
1880        }
1881
1882        if let Some((src_region_index, dst_region_index)) = overlap_indices {
1883            return Err(Box::new(ValidationError {
1884                problem: format!(
1885                    "`src_buffer.buffer()` is equal to `dst_buffer.buffer()`, and \
1886                    the source of `regions[{}]` overlaps with the destination of `regions[{}]`",
1887                    src_region_index, dst_region_index
1888                )
1889                .into(),
1890                vuids: &["VUID-VkCopyBufferInfo2-pRegions-00117"],
1891                ..Default::default()
1892            }));
1893        }
1894
1895        Ok(())
1896    }
1897
1898    pub(crate) fn to_vk2<'a>(
1899        &self,
1900        regions_vk: &'a [ash::vk::BufferCopy2<'static>],
1901    ) -> ash::vk::CopyBufferInfo2<'a> {
1902        let &Self {
1903            ref src_buffer,
1904            ref dst_buffer,
1905            regions: _,
1906            _ne: _,
1907        } = self;
1908
1909        ash::vk::CopyBufferInfo2::default()
1910            .src_buffer(src_buffer.buffer().handle())
1911            .dst_buffer(dst_buffer.buffer().handle())
1912            .regions(regions_vk)
1913    }
1914
1915    pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::BufferCopy2<'static>; 8]> {
1916        let &Self {
1917            ref src_buffer,
1918            ref dst_buffer,
1919            ref regions,
1920            _ne: _,
1921        } = self;
1922
1923        regions
1924            .iter()
1925            .map(|region| {
1926                let mut region_vk = region.to_vk2();
1927                region_vk.src_offset += src_buffer.offset();
1928                region_vk.dst_offset += dst_buffer.offset();
1929                region_vk
1930            })
1931            .collect()
1932    }
1933
1934    pub(crate) fn to_vk(&self) -> CopyBufferInfoVk {
1935        let &Self {
1936            ref src_buffer,
1937            ref dst_buffer,
1938            regions: _,
1939            _ne: _,
1940        } = self;
1941
1942        CopyBufferInfoVk {
1943            src_buffer_vk: src_buffer.buffer().handle(),
1944            dst_buffer_vk: dst_buffer.buffer().handle(),
1945        }
1946    }
1947
1948    pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::BufferCopy; 8]> {
1949        let &Self {
1950            ref src_buffer,
1951            ref dst_buffer,
1952            ref regions,
1953            _ne: _,
1954        } = self;
1955
1956        regions
1957            .iter()
1958            .map(|region| {
1959                let mut region_vk = region.to_vk();
1960                region_vk.src_offset += src_buffer.offset();
1961                region_vk.dst_offset += dst_buffer.offset();
1962                region_vk
1963            })
1964            .collect()
1965    }
1966}
1967
1968pub(crate) struct CopyBufferInfoVk {
1969    pub(crate) src_buffer_vk: ash::vk::Buffer,
1970    pub(crate) dst_buffer_vk: ash::vk::Buffer,
1971}
1972
1973/// Parameters to copy data from a buffer to another buffer, with type information.
1974///
1975/// The fields of `regions` represent elements of `T`.
1976#[derive(Clone, Debug)]
1977pub struct CopyBufferInfoTyped<T> {
1978    /// The buffer to copy from.
1979    ///
1980    /// There is no default value.
1981    pub src_buffer: Subbuffer<[T]>,
1982
1983    /// The buffer to copy to.
1984    ///
1985    /// There is no default value.
1986    pub dst_buffer: Subbuffer<[T]>,
1987
1988    /// The regions of both buffers to copy between, specified in elements of `T`.
1989    ///
1990    /// The default value is a single region, with zero offsets and a `size` equal to the smallest
1991    /// of the two buffers.
1992    pub regions: SmallVec<[BufferCopy; 1]>,
1993
1994    pub _ne: crate::NonExhaustive,
1995}
1996
1997impl<T> CopyBufferInfoTyped<T> {
1998    /// Returns a `CopyBufferInfoTyped` with the specified `src_buffer` and `dst_buffer`.
1999    pub fn buffers(src_buffer: Subbuffer<[T]>, dst_buffer: Subbuffer<[T]>) -> Self {
2000        let region = BufferCopy {
2001            size: min(src_buffer.len(), dst_buffer.len()),
2002            ..Default::default()
2003        };
2004
2005        Self {
2006            src_buffer,
2007            dst_buffer,
2008            regions: smallvec![region],
2009            _ne: crate::NonExhaustive(()),
2010        }
2011    }
2012}
2013
2014impl<T> From<CopyBufferInfoTyped<T>> for CopyBufferInfo {
2015    fn from(typed: CopyBufferInfoTyped<T>) -> Self {
2016        let CopyBufferInfoTyped {
2017            src_buffer,
2018            dst_buffer,
2019            mut regions,
2020            _ne: _,
2021        } = typed;
2022
2023        for region in &mut regions {
2024            region.src_offset *= size_of::<T>() as DeviceSize;
2025            region.dst_offset *= size_of::<T>() as DeviceSize;
2026            region.size *= size_of::<T>() as DeviceSize;
2027        }
2028
2029        Self {
2030            src_buffer: src_buffer.as_bytes().clone(),
2031            dst_buffer: dst_buffer.as_bytes().clone(),
2032            regions,
2033            _ne: crate::NonExhaustive(()),
2034        }
2035    }
2036}
2037
2038/// A region of data to copy between buffers.
2039#[derive(Clone, Debug)]
2040pub struct BufferCopy {
2041    /// The offset in bytes or elements from the start of `src_buffer` that copying will
2042    /// start from.
2043    ///
2044    /// The default value is `0`.
2045    pub src_offset: DeviceSize,
2046
2047    /// The offset in bytes or elements from the start of `dst_buffer` that copying will
2048    /// start from.
2049    ///
2050    /// The default value is `0`.
2051    pub dst_offset: DeviceSize,
2052
2053    /// The number of bytes or elements to copy.
2054    ///
2055    /// The default value is `0`, which must be overridden.
2056    pub size: DeviceSize,
2057
2058    pub _ne: crate::NonExhaustive,
2059}
2060
2061impl Default for BufferCopy {
2062    #[inline]
2063    fn default() -> Self {
2064        Self {
2065            src_offset: 0,
2066            dst_offset: 0,
2067            size: 0,
2068            _ne: crate::NonExhaustive(()),
2069        }
2070    }
2071}
2072
2073impl BufferCopy {
2074    pub(crate) fn validate(&self, _device: &Device) -> Result<(), Box<ValidationError>> {
2075        let &Self {
2076            src_offset: _,
2077            dst_offset: _,
2078            size,
2079            _ne: _,
2080        } = self;
2081
2082        if size == 0 {
2083            return Err(Box::new(ValidationError {
2084                context: "size".into(),
2085                problem: "is zero".into(),
2086                vuids: &["VUID-VkBufferCopy2-size-01988"],
2087                ..Default::default()
2088            }));
2089        }
2090
2091        Ok(())
2092    }
2093
2094    pub(crate) fn to_vk2(&self) -> ash::vk::BufferCopy2<'static> {
2095        let &Self {
2096            src_offset,
2097            dst_offset,
2098            size,
2099            _ne,
2100        } = self;
2101
2102        ash::vk::BufferCopy2::default()
2103            .src_offset(src_offset)
2104            .dst_offset(dst_offset)
2105            .size(size)
2106    }
2107
2108    pub(crate) fn to_vk(&self) -> ash::vk::BufferCopy {
2109        let &Self {
2110            src_offset,
2111            dst_offset,
2112            size,
2113            _ne,
2114        } = self;
2115
2116        ash::vk::BufferCopy {
2117            src_offset,
2118            dst_offset,
2119            size,
2120        }
2121    }
2122}
2123
2124/// Parameters to copy data from an image to another image.
2125#[derive(Clone, Debug)]
2126pub struct CopyImageInfo {
2127    /// The image to copy from.
2128    ///
2129    /// There is no default value.
2130    pub src_image: Arc<Image>,
2131
2132    /// The layout used for `src_image` during the copy operation.
2133    ///
2134    /// The following layouts are allowed:
2135    /// - [`ImageLayout::TransferSrcOptimal`]
2136    /// - [`ImageLayout::General`]
2137    ///
2138    /// The default value is [`ImageLayout::TransferSrcOptimal`].
2139    pub src_image_layout: ImageLayout,
2140
2141    /// The image to copy to.
2142    ///
2143    /// There is no default value.
2144    pub dst_image: Arc<Image>,
2145
2146    /// The layout used for `dst_image` during the copy operation.
2147    ///
2148    /// The following layouts are allowed:
2149    /// - [`ImageLayout::TransferDstOptimal`]
2150    /// - [`ImageLayout::General`]
2151    ///
2152    /// The default value is [`ImageLayout::TransferDstOptimal`].
2153    pub dst_image_layout: ImageLayout,
2154
2155    /// The regions of both images to copy between.
2156    ///
2157    /// The default value is a single region, covering the first mip level, and the smallest of the
2158    /// array layers and extent of the two images. All aspects of each image are selected, or
2159    /// `plane0` if the image is multi-planar.
2160    pub regions: SmallVec<[ImageCopy; 1]>,
2161
2162    pub _ne: crate::NonExhaustive,
2163}
2164
2165impl CopyImageInfo {
2166    /// Returns a `CopyImageInfo` with the specified `src_image` and `dst_image`.
2167    #[inline]
2168    pub fn images(src_image: Arc<Image>, dst_image: Arc<Image>) -> Self {
2169        let min_array_layers = src_image.array_layers().min(dst_image.array_layers());
2170        let region = ImageCopy {
2171            src_subresource: ImageSubresourceLayers {
2172                array_layers: 0..min_array_layers,
2173                ..src_image.subresource_layers()
2174            },
2175            dst_subresource: ImageSubresourceLayers {
2176                array_layers: 0..min_array_layers,
2177                ..dst_image.subresource_layers()
2178            },
2179            extent: {
2180                let src_extent = src_image.extent();
2181                let dst_extent = dst_image.extent();
2182
2183                [
2184                    src_extent[0].min(dst_extent[0]),
2185                    src_extent[1].min(dst_extent[1]),
2186                    src_extent[2].min(dst_extent[2]),
2187                ]
2188            },
2189            ..Default::default()
2190        };
2191
2192        Self {
2193            src_image,
2194            src_image_layout: ImageLayout::TransferSrcOptimal,
2195            dst_image,
2196            dst_image_layout: ImageLayout::TransferDstOptimal,
2197            regions: smallvec![region],
2198            _ne: crate::NonExhaustive(()),
2199        }
2200    }
2201
2202    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
2203        let &Self {
2204            ref src_image,
2205            src_image_layout,
2206            ref dst_image,
2207            dst_image_layout,
2208            ref regions,
2209            _ne: _,
2210        } = self;
2211
2212        src_image_layout.validate_device(device).map_err(|err| {
2213            err.add_context("src_image_layout")
2214                .set_vuids(&["VUID-VkCopyImageInfo2-srcImageLayout-parameter"])
2215        })?;
2216
2217        dst_image_layout.validate_device(device).map_err(|err| {
2218            err.add_context("dst_image_layout")
2219                .set_vuids(&["VUID-VkCopyImageInfo2-dstImageLayout-parameter"])
2220        })?;
2221
2222        // VUID-VkCopyImageInfo2-commonparent
2223        assert_eq!(device, src_image.device().as_ref());
2224        assert_eq!(device, dst_image.device().as_ref());
2225
2226        let src_image_format = src_image.format();
2227        let src_image_format_aspects = src_image_format.aspects();
2228        let src_image_format_planes = src_image_format.planes();
2229        let src_image_format_subsampled_extent = src_image_format
2230            .ycbcr_chroma_sampling()
2231            .map_or(src_image.extent(), |s| {
2232                s.subsampled_extent(src_image.extent())
2233            });
2234
2235        let dst_image_format = dst_image.format();
2236        let dst_image_format_aspects = dst_image_format.aspects();
2237        let dst_image_format_planes = dst_image_format.planes();
2238        let dst_image_format_subsampled_extent = dst_image_format
2239            .ycbcr_chroma_sampling()
2240            .map_or(dst_image.extent(), |s| {
2241                s.subsampled_extent(dst_image.extent())
2242            });
2243
2244        if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
2245            if !src_image
2246                .format_features()
2247                .intersects(FormatFeatures::TRANSFER_SRC)
2248            {
2249                return Err(Box::new(ValidationError {
2250                    context: "src_image.format_features()".into(),
2251                    problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(),
2252                    vuids: &["VUID-VkCopyImageInfo2-srcImage-01995"],
2253                    ..Default::default()
2254                }));
2255            }
2256
2257            if !dst_image
2258                .format_features()
2259                .intersects(FormatFeatures::TRANSFER_DST)
2260            {
2261                return Err(Box::new(ValidationError {
2262                    context: "dst_image.format_features()".into(),
2263                    problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(),
2264                    vuids: &["VUID-VkCopyImageInfo2-dstImage-01996"],
2265                    ..Default::default()
2266                }));
2267            }
2268        }
2269
2270        if src_image.samples() != dst_image.samples() {
2271            return Err(Box::new(ValidationError {
2272                problem: "`src_image.samples()` does not equal `dst_image.samples()`".into(),
2273                vuids: &["VUID-VkCopyImageInfo2-srcImage-00136"],
2274                ..Default::default()
2275            }));
2276        }
2277
2278        if !matches!(
2279            src_image_layout,
2280            ImageLayout::TransferSrcOptimal | ImageLayout::General
2281        ) {
2282            return Err(Box::new(ValidationError {
2283                context: "src_image_layout".into(),
2284                problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`"
2285                    .into(),
2286                vuids: &["VUID-VkCopyImageInfo2-srcImageLayout-01917"],
2287                ..Default::default()
2288            }));
2289        }
2290
2291        if !matches!(
2292            dst_image_layout,
2293            ImageLayout::TransferDstOptimal | ImageLayout::General
2294        ) {
2295            return Err(Box::new(ValidationError {
2296                context: "dst_image_layout".into(),
2297                problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
2298                    .into(),
2299                vuids: &["VUID-VkCopyImageInfo2-dstImageLayout-01395"],
2300                ..Default::default()
2301            }));
2302        }
2303
2304        if src_image.image_type() != dst_image.image_type() {
2305            if !(matches!(src_image.image_type(), ImageType::Dim2d | ImageType::Dim3d)
2306                && matches!(dst_image.image_type(), ImageType::Dim2d | ImageType::Dim3d))
2307            {
2308                return Err(Box::new(ValidationError {
2309                    problem: "`src_image.image_type()` does not equal `dst_image.image_type()`, \
2310                        but they are not both `ImageType::Dim2d` or `ImageType::Dim3d`"
2311                        .into(),
2312                    vuids: &["VUID-VkCopyImageInfo2-srcImage-07743"],
2313                    ..Default::default()
2314                }));
2315            }
2316
2317            if !(device.api_version() >= Version::V1_1
2318                || device.enabled_extensions().khr_maintenance1)
2319            {
2320                return Err(Box::new(ValidationError {
2321                    problem: "`src_image.image_type()` does not equal `dst_image.image_type()`, \
2322                        and are both `ImageType::Dim2d` or `ImageType::Dim3d`"
2323                        .into(),
2324                    requires_one_of: RequiresOneOf(&[
2325                        RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]),
2326                        RequiresAllOf(&[Requires::DeviceExtension("khr_maintenance1")]),
2327                    ]),
2328                    vuids: &["VUID-VkCopyImageInfo2-apiVersion-07933"],
2329                    ..Default::default()
2330                }));
2331            }
2332        }
2333
2334        let is_same_image = src_image == dst_image;
2335        let mut overlap_subresource_indices = None;
2336        let mut overlap_extent_indices = None;
2337
2338        for (region_index, region) in regions.iter().enumerate() {
2339            region
2340                .validate(device)
2341                .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
2342
2343            let &ImageCopy {
2344                ref src_subresource,
2345                src_offset,
2346                ref dst_subresource,
2347                dst_offset,
2348                extent,
2349                _ne,
2350            } = region;
2351
2352            /*
2353               Check src
2354            */
2355
2356            if src_subresource.mip_level >= src_image.mip_levels() {
2357                return Err(Box::new(ValidationError {
2358                    problem: format!(
2359                        "`regions[{}].src_subresource.mip_level` is not less than \
2360                        `src_image.mip_levels()`",
2361                        region_index
2362                    )
2363                    .into(),
2364                    vuids: &["VUID-VkCopyImageInfo2-srcSubresource-07967"],
2365                    ..Default::default()
2366                }));
2367            }
2368
2369            let mut src_subresource_format = src_image_format;
2370            let mut src_subresource_extent =
2371                mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap();
2372
2373            if src_image_format_planes.is_empty() {
2374                if !src_image_format_aspects.contains(src_subresource.aspects) {
2375                    return Err(Box::new(ValidationError {
2376                        problem: format!(
2377                            "`regions[{}].src_subresource.aspects` is not a subset of \
2378                            `src_image.format().aspects()`",
2379                            region_index
2380                        )
2381                        .into(),
2382                        vuids: &["VUID-VkCopyImageInfo2-aspectMask-00142"],
2383                        ..Default::default()
2384                    }));
2385                }
2386            } else if src_image_format_planes.len() == 2 {
2387                match src_subresource.aspects {
2388                    ImageAspects::PLANE_0 => {
2389                        src_subresource_format = src_image_format_planes[0];
2390                    }
2391                    ImageAspects::PLANE_1 => {
2392                        src_subresource_format = src_image_format_planes[1];
2393                        src_subresource_extent = src_image_format_subsampled_extent;
2394                    }
2395                    _ => {
2396                        return Err(Box::new(ValidationError {
2397                            problem: format!(
2398                                "`src_image.format()` is a multi-planar format with two planes, \
2399                                but `regions[{}].src_subresource.aspect` is not \
2400                                `ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`",
2401                                region_index,
2402                            )
2403                            .into(),
2404                            vuids: &[
2405                                "VUID-VkCopyImageInfo2-srcImage-08713",
2406                                "VUID-VkCopyImageInfo2-aspectMask-00142",
2407                            ],
2408                            ..Default::default()
2409                        }));
2410                    }
2411                }
2412            } else if src_image_format_planes.len() == 3 {
2413                match src_subresource.aspects {
2414                    ImageAspects::PLANE_0 => {
2415                        src_subresource_format = src_image_format_planes[0];
2416                    }
2417                    ImageAspects::PLANE_1 => {
2418                        src_subresource_format = src_image_format_planes[1];
2419                        src_subresource_extent = src_image_format_subsampled_extent;
2420                    }
2421                    ImageAspects::PLANE_2 => {
2422                        src_subresource_format = src_image_format_planes[2];
2423                        src_subresource_extent = src_image_format_subsampled_extent;
2424                    }
2425                    _ => {
2426                        return Err(Box::new(ValidationError {
2427                            problem: format!(
2428                                "`src_image.format()` is a multi-planar format with three planes, \
2429                                but `regions[{}].src_subresource.aspect` is not \
2430                                `ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \
2431                                `ImageAspects::PLANE_2`",
2432                                region_index,
2433                            )
2434                            .into(),
2435                            vuids: &[
2436                                "VUID-VkCopyImageInfo2-srcImage-08713",
2437                                "VUID-VkCopyImageInfo2-aspectMask-00142",
2438                            ],
2439                            ..Default::default()
2440                        }));
2441                    }
2442                }
2443            }
2444
2445            match src_image.image_type() {
2446                ImageType::Dim1d => {
2447                    if src_offset[1] != 0 {
2448                        return Err(Box::new(ValidationError {
2449                            problem: format!(
2450                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
2451                                `regions[{}].src_offset[1]` is not 0",
2452                                region_index,
2453                            )
2454                            .into(),
2455                            vuids: &["VUID-VkCopyImageInfo2-srcImage-00146"],
2456                            ..Default::default()
2457                        }));
2458                    }
2459
2460                    if extent[1] != 1 {
2461                        return Err(Box::new(ValidationError {
2462                            problem: format!(
2463                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
2464                                `regions[{}].extent[1]` is not 1",
2465                                region_index,
2466                            )
2467                            .into(),
2468                            vuids: &["VUID-VkCopyImageInfo2-srcImage-00146"],
2469                            ..Default::default()
2470                        }));
2471                    }
2472
2473                    if src_offset[2] != 0 {
2474                        return Err(Box::new(ValidationError {
2475                            problem: format!(
2476                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
2477                                `regions[{}].src_offset[2]` is not 0",
2478                                region_index,
2479                            )
2480                            .into(),
2481                            vuids: &["VUID-VkCopyImageInfo2-srcImage-01785"],
2482                            ..Default::default()
2483                        }));
2484                    }
2485
2486                    if extent[2] != 1 {
2487                        return Err(Box::new(ValidationError {
2488                            problem: format!(
2489                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
2490                                `regions[{}].extent[2]` is not 1",
2491                                region_index,
2492                            )
2493                            .into(),
2494                            vuids: &["VUID-VkCopyImageInfo2-srcImage-01785"],
2495                            ..Default::default()
2496                        }));
2497                    }
2498                }
2499                ImageType::Dim2d => {
2500                    if src_offset[2] != 0 {
2501                        return Err(Box::new(ValidationError {
2502                            problem: format!(
2503                                "`src_image.image_type()` is `ImageType::Dim2d`, but \
2504                                `regions[{}].src_offset[2]` is not 0",
2505                                region_index,
2506                            )
2507                            .into(),
2508                            vuids: &["VUID-VkCopyImageInfo2-srcImage-01787"],
2509                            ..Default::default()
2510                        }));
2511                    }
2512                }
2513                ImageType::Dim3d => {
2514                    if src_subresource.array_layers != (0..1) {
2515                        return Err(Box::new(ValidationError {
2516                            problem: format!(
2517                                "`src_image.image_type()` is `ImageType::Dim3d`, but \
2518                                `regions[{}].src_subresource.array_layers` is not `0..1`",
2519                                region_index,
2520                            )
2521                            .into(),
2522                            vuids: &[
2523                                "VUID-VkCopyImageInfo2-srcImage-04443",
2524                                "VUID-VkCopyImageInfo2-apiVersion-07932",
2525                            ],
2526                            ..Default::default()
2527                        }));
2528                    }
2529                }
2530            }
2531
2532            if src_subresource.array_layers.end > src_image.array_layers() {
2533                return Err(Box::new(ValidationError {
2534                    problem: format!(
2535                        "`regions[{}].src_subresource.array_layers.end` is not less than \
2536                        `src_image.array_layers()`",
2537                        region_index
2538                    )
2539                    .into(),
2540                    vuids: &["VUID-VkCopyImageInfo2-srcSubresource-07968"],
2541                    ..Default::default()
2542                }));
2543            }
2544
2545            if src_offset[0] + extent[0] > src_subresource_extent[0] {
2546                return Err(Box::new(ValidationError {
2547                    problem: format!(
2548                        "`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is greater \
2549                        than coordinate 0 of the extent of the subresource of `src_image` \
2550                        selected by `regions[{0}].src_subresource`",
2551                        region_index,
2552                    )
2553                    .into(),
2554                    vuids: &["VUID-VkCopyImageInfo2-srcOffset-00144"],
2555                    ..Default::default()
2556                }));
2557            }
2558
2559            if src_offset[1] + extent[1] > src_subresource_extent[1] {
2560                return Err(Box::new(ValidationError {
2561                    problem: format!(
2562                        "`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is greater \
2563                        than coordinate 1 of the extent of the subresource of `src_image` \
2564                        selected by `regions[{0}].src_subresource`",
2565                        region_index,
2566                    )
2567                    .into(),
2568                    vuids: &["VUID-VkCopyImageInfo2-srcOffset-00145"],
2569                    ..Default::default()
2570                }));
2571            }
2572
2573            if src_offset[2] + extent[2] > src_subresource_extent[2] {
2574                return Err(Box::new(ValidationError {
2575                    problem: format!(
2576                        "`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is greater \
2577                        than coordinate 2 of the extent of the subresource of `src_image` \
2578                        selected by `regions[{0}].src_subresource`",
2579                        region_index,
2580                    )
2581                    .into(),
2582                    vuids: &["VUID-VkCopyImageInfo2-srcOffset-00147"],
2583                    ..Default::default()
2584                }));
2585            }
2586
2587            let src_subresource_format_block_extent = src_subresource_format.block_extent();
2588
2589            if src_offset[0] % src_subresource_format_block_extent[0] != 0 {
2590                return Err(Box::new(ValidationError {
2591                    problem: format!(
2592                        "`regions[{0}].src_offset[0]` is not a multiple of coordinate 0 of the \
2593                        block extent of the format of the subresource of `src_image` \
2594                        selected by `regions[{0}].src_subresource`",
2595                        region_index,
2596                    )
2597                    .into(),
2598                    vuids: &["VUID-VkCopyImageInfo2-pRegions-07278"],
2599                    ..Default::default()
2600                }));
2601            }
2602
2603            if src_offset[1] % src_subresource_format_block_extent[1] != 0 {
2604                return Err(Box::new(ValidationError {
2605                    problem: format!(
2606                        "`regions[{0}].src_offset[1]` is not a multiple of coordinate 1 of the \
2607                        block extent of the format of the subresource of `src_image` \
2608                        selected by `regions[{0}].src_subresource`",
2609                        region_index,
2610                    )
2611                    .into(),
2612                    vuids: &["VUID-VkCopyImageInfo2-pRegions-07279"],
2613                    ..Default::default()
2614                }));
2615            }
2616
2617            if src_offset[2] % src_subresource_format_block_extent[2] != 0 {
2618                return Err(Box::new(ValidationError {
2619                    problem: format!(
2620                        "`regions[{0}].src_offset[2]` is not a multiple of coordinate 2 of the \
2621                        block extent of the format of the subresource of `src_image` \
2622                        selected by `regions[{0}].src_subresource`",
2623                        region_index,
2624                    )
2625                    .into(),
2626                    vuids: &["VUID-VkCopyImageInfo2-pRegions-07280"],
2627                    ..Default::default()
2628                }));
2629            }
2630
2631            if src_offset[0] + extent[0] != src_subresource_extent[0]
2632                && (src_offset[0] + extent[0]) % src_subresource_format_block_extent[0] != 0
2633            {
2634                return Err(Box::new(ValidationError {
2635                    problem: format!(
2636                        "`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is not \
2637                        equal to the extent of the subresource of `src_image` \
2638                        selected by `regions[{0}].src_subresource`, but \
2639                        it is also not a multiple of coordinate 0 of the block extent of the \
2640                        format of that subresource",
2641                        region_index,
2642                    )
2643                    .into(),
2644                    vuids: &["VUID-VkCopyImageInfo2-srcImage-01728"],
2645                    ..Default::default()
2646                }));
2647            }
2648
2649            if src_offset[1] + extent[1] != src_subresource_extent[1]
2650                && (src_offset[1] + extent[1]) % src_subresource_format_block_extent[1] != 0
2651            {
2652                return Err(Box::new(ValidationError {
2653                    problem: format!(
2654                        "`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is not \
2655                        equal to the extent of the subresource of `src_image` \
2656                        selected by `regions[{0}].src_subresource`, but \
2657                        it is also not a multiple of coordinate 1 of the block extent of the \
2658                        format of that subresource",
2659                        region_index,
2660                    )
2661                    .into(),
2662                    vuids: &["VUID-VkCopyImageInfo2-srcImage-01729"],
2663                    ..Default::default()
2664                }));
2665            }
2666
2667            if src_offset[2] + extent[2] != src_subresource_extent[2]
2668                && (src_offset[2] + extent[2]) % src_subresource_format_block_extent[2] != 0
2669            {
2670                return Err(Box::new(ValidationError {
2671                    problem: format!(
2672                        "`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is not \
2673                        equal to the extent of the subresource of `src_image` \
2674                        selected by `regions[{0}].src_subresource`, but \
2675                        it is also not a multiple of coordinate 2 of the block extent of the \
2676                        format of that subresource",
2677                        region_index,
2678                    )
2679                    .into(),
2680                    vuids: &["VUID-VkCopyImageInfo2-srcImage-01730"],
2681                    ..Default::default()
2682                }));
2683            }
2684
2685            if !(src_subresource.aspects - ImageAspects::STENCIL).is_empty()
2686                && !src_image.usage().intersects(ImageUsage::TRANSFER_SRC)
2687            {
2688                return Err(Box::new(ValidationError {
2689                    problem: format!(
2690                        "`regions[{0}].src_subresource.aspects` contains aspects other than \
2691                        `ImageAspects::STENCIL`, but \
2692                        `src_image.usage()` does not contain `ImageUsage::TRANSFER_SRC`",
2693                        region_index,
2694                    )
2695                    .into(),
2696                    vuids: &["VUID-VkCopyImageInfo2-aspect-06662"],
2697                    ..Default::default()
2698                }));
2699            }
2700
2701            if src_subresource.aspects.intersects(ImageAspects::STENCIL)
2702                && !src_image
2703                    .stencil_usage()
2704                    .unwrap_or(src_image.usage())
2705                    .intersects(ImageUsage::TRANSFER_SRC)
2706            {
2707                return Err(Box::new(ValidationError {
2708                    problem: format!(
2709                        "`regions[{0}].src_subresource.aspects` contains \
2710                        `ImageAspects::STENCIL`, but \
2711                        `src_image.stencil_usage()` does not contain `ImageUsage::TRANSFER_SRC`",
2712                        region_index,
2713                    )
2714                    .into(),
2715                    vuids: &["VUID-VkCopyImageInfo2-aspect-06664"],
2716                    ..Default::default()
2717                }));
2718            }
2719
2720            /*
2721               Check dst
2722            */
2723
2724            if dst_subresource.mip_level >= dst_image.mip_levels() {
2725                return Err(Box::new(ValidationError {
2726                    problem: format!(
2727                        "`regions[{}].dst_subresource.mip_level` is not less than \
2728                        `dst_image.mip_levels()`",
2729                        region_index
2730                    )
2731                    .into(),
2732                    vuids: &["VUID-VkCopyImageInfo2-dstSubresource-07967"],
2733                    ..Default::default()
2734                }));
2735            }
2736
2737            let mut dst_subresource_format = dst_image_format;
2738            let mut dst_subresource_extent =
2739                mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap();
2740
2741            if dst_image_format_planes.is_empty() {
2742                if !dst_image_format_aspects.contains(dst_subresource.aspects) {
2743                    return Err(Box::new(ValidationError {
2744                        problem: format!(
2745                            "`regions[{}].dst_subresource.aspects` is not a subset of \
2746                            `dst_image.format().aspects()`",
2747                            region_index
2748                        )
2749                        .into(),
2750                        vuids: &["VUID-VkCopyImageInfo2-aspectMask-00143"],
2751                        ..Default::default()
2752                    }));
2753                }
2754            } else if dst_image_format_planes.len() == 2 {
2755                match dst_subresource.aspects {
2756                    ImageAspects::PLANE_0 => {
2757                        dst_subresource_format = dst_image_format_planes[0];
2758                    }
2759                    ImageAspects::PLANE_1 => {
2760                        dst_subresource_format = dst_image_format_planes[1];
2761                        dst_subresource_extent = dst_image_format_subsampled_extent;
2762                    }
2763                    _ => {
2764                        return Err(Box::new(ValidationError {
2765                            problem: format!(
2766                                "`dst_image.format()` is a multi-planar format with two planes, \
2767                                but `regions[{}].dst_subresource.aspect` is not \
2768                                `ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`",
2769                                region_index,
2770                            )
2771                            .into(),
2772                            vuids: &[
2773                                "VUID-VkCopyImageInfo2-dstImage-08714",
2774                                "VUID-VkCopyImageInfo2-aspectMask-00143",
2775                            ],
2776                            ..Default::default()
2777                        }));
2778                    }
2779                }
2780            } else if dst_image_format_planes.len() == 3 {
2781                match dst_subresource.aspects {
2782                    ImageAspects::PLANE_0 => {
2783                        dst_subresource_format = dst_image_format_planes[0];
2784                    }
2785                    ImageAspects::PLANE_1 => {
2786                        dst_subresource_format = dst_image_format_planes[1];
2787                        dst_subresource_extent = dst_image_format_subsampled_extent;
2788                    }
2789                    ImageAspects::PLANE_2 => {
2790                        dst_subresource_format = dst_image_format_planes[2];
2791                        dst_subresource_extent = dst_image_format_subsampled_extent;
2792                    }
2793                    _ => {
2794                        return Err(Box::new(ValidationError {
2795                            problem: format!(
2796                                "`dst_image.format()` is a multi-planar format with three planes, \
2797                                but `regions[{}].dst_subresource.aspect` is not \
2798                                `ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \
2799                                `ImageAspects::PLANE_2`",
2800                                region_index,
2801                            )
2802                            .into(),
2803                            vuids: &[
2804                                "VUID-VkCopyImageInfo2-dstImage-08714",
2805                                "VUID-VkCopyImageInfo2-aspectMask-00143",
2806                            ],
2807                            ..Default::default()
2808                        }));
2809                    }
2810                }
2811            }
2812
2813            match dst_image.image_type() {
2814                ImageType::Dim1d => {
2815                    if dst_offset[1] != 0 {
2816                        return Err(Box::new(ValidationError {
2817                            problem: format!(
2818                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
2819                                `regions[{}].dst_offset[1]` is not 0",
2820                                region_index,
2821                            )
2822                            .into(),
2823                            vuids: &["VUID-VkCopyImageInfo2-dstImage-00152"],
2824                            ..Default::default()
2825                        }));
2826                    }
2827
2828                    if extent[1] != 1 {
2829                        return Err(Box::new(ValidationError {
2830                            problem: format!(
2831                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
2832                                `regions[{}].extent[1]` is not 1",
2833                                region_index,
2834                            )
2835                            .into(),
2836                            vuids: &["VUID-VkCopyImageInfo2-dstImage-00152"],
2837                            ..Default::default()
2838                        }));
2839                    }
2840
2841                    if dst_offset[2] != 0 {
2842                        return Err(Box::new(ValidationError {
2843                            problem: format!(
2844                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
2845                                `regions[{}].dst_offset[2]` is not 0",
2846                                region_index,
2847                            )
2848                            .into(),
2849                            vuids: &["VUID-VkCopyImageInfo2-dstImage-01786"],
2850                            ..Default::default()
2851                        }));
2852                    }
2853
2854                    if extent[2] != 1 {
2855                        return Err(Box::new(ValidationError {
2856                            problem: format!(
2857                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
2858                                `regions[{}].extent[2]` is not 1",
2859                                region_index,
2860                            )
2861                            .into(),
2862                            vuids: &["VUID-VkCopyImageInfo2-dstImage-01786"],
2863                            ..Default::default()
2864                        }));
2865                    }
2866                }
2867                ImageType::Dim2d => {
2868                    if dst_offset[2] != 0 {
2869                        return Err(Box::new(ValidationError {
2870                            problem: format!(
2871                                "`dst_image.image_type()` is `ImageType::Dim2d`, but \
2872                                `regions[{}].dst_offset[2]` is not 0",
2873                                region_index,
2874                            )
2875                            .into(),
2876                            vuids: &["VUID-VkCopyImageInfo2-dstImage-01788"],
2877                            ..Default::default()
2878                        }));
2879                    }
2880                }
2881                ImageType::Dim3d => {
2882                    if dst_subresource.array_layers != (0..1) {
2883                        return Err(Box::new(ValidationError {
2884                            problem: format!(
2885                                "`dst_image.image_type()` is  `ImageType::Dim3d`, but \
2886                                `regions[{}].dst_subresource.array_layers` is not `0..1`",
2887                                region_index,
2888                            )
2889                            .into(),
2890                            vuids: &[
2891                                "VUID-VkCopyImageInfo2-dstImage-04444",
2892                                "VUID-VkCopyImageInfo2-apiVersion-07932",
2893                            ],
2894                            ..Default::default()
2895                        }));
2896                    }
2897                }
2898            }
2899
2900            if dst_subresource.array_layers.end > dst_image.array_layers() {
2901                return Err(Box::new(ValidationError {
2902                    problem: format!(
2903                        "`regions[{}].dst_subresource.array_layers.end` is not less than \
2904                        `dst_image.array_layers()`",
2905                        region_index
2906                    )
2907                    .into(),
2908                    vuids: &["VUID-VkCopyImageInfo2-dstSubresource-07968"],
2909                    ..Default::default()
2910                }));
2911            }
2912
2913            if dst_offset[0] + extent[0] > dst_subresource_extent[0] {
2914                return Err(Box::new(ValidationError {
2915                    problem: format!(
2916                        "`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is greater \
2917                        than coordinate 0 of the extent of the subresource of `dst_image` \
2918                        selected by `regions[{0}].dst_subresource`",
2919                        region_index,
2920                    )
2921                    .into(),
2922                    vuids: &["VUID-VkCopyImageInfo2-dstOffset-00150"],
2923                    ..Default::default()
2924                }));
2925            }
2926
2927            if dst_offset[1] + extent[1] > dst_subresource_extent[1] {
2928                return Err(Box::new(ValidationError {
2929                    problem: format!(
2930                        "`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is greater \
2931                        than coordinate 1 of the extent of the subresource of `dst_image` \
2932                        selected by `regions[{0}].dst_subresource`",
2933                        region_index,
2934                    )
2935                    .into(),
2936                    vuids: &["VUID-VkCopyImageInfo2-dstOffset-00151"],
2937                    ..Default::default()
2938                }));
2939            }
2940
2941            if dst_offset[2] + extent[2] > dst_subresource_extent[2] {
2942                return Err(Box::new(ValidationError {
2943                    problem: format!(
2944                        "`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is greater \
2945                        than coordinate 2 of the extent of the subresource of `dst_image` \
2946                        selected by `regions[{0}].dst_subresource`",
2947                        region_index,
2948                    )
2949                    .into(),
2950                    vuids: &["VUID-VkCopyImageInfo2-dstOffset-00153"],
2951                    ..Default::default()
2952                }));
2953            }
2954
2955            let dst_subresource_format_block_extent = dst_subresource_format.block_extent();
2956
2957            if dst_offset[0] % dst_subresource_format_block_extent[0] != 0 {
2958                return Err(Box::new(ValidationError {
2959                    problem: format!(
2960                        "`regions[{0}].dst_offset[0]` is not a multiple of coordinate 0 of the \
2961                        block extent of the format of the subresource of `dst_image` \
2962                        selected by `regions[{0}].dst_subresource`",
2963                        region_index,
2964                    )
2965                    .into(),
2966                    vuids: &["VUID-VkCopyImageInfo2-pRegions-07281"],
2967                    ..Default::default()
2968                }));
2969            }
2970
2971            if dst_offset[1] % dst_subresource_format_block_extent[1] != 0 {
2972                return Err(Box::new(ValidationError {
2973                    problem: format!(
2974                        "`regions[{0}].dst_offset[1]` is not a multiple of coordinate 1 of the \
2975                        block extent of the format of the subresource of `dst_image` \
2976                        selected by `regions[{0}].dst_subresource`",
2977                        region_index,
2978                    )
2979                    .into(),
2980                    vuids: &["VUID-VkCopyImageInfo2-pRegions-07282"],
2981                    ..Default::default()
2982                }));
2983            }
2984
2985            if dst_offset[2] % dst_subresource_format_block_extent[2] != 0 {
2986                return Err(Box::new(ValidationError {
2987                    problem: format!(
2988                        "`regions[{0}].dst_offset[2]` is not a multiple of coordinate 2 of the \
2989                        block extent of the format of the subresource of `dst_image` \
2990                        selected by `regions[{0}].dst_subresource`",
2991                        region_index,
2992                    )
2993                    .into(),
2994                    vuids: &["VUID-VkCopyImageInfo2-pRegions-07283"],
2995                    ..Default::default()
2996                }));
2997            }
2998
2999            if dst_offset[0] + extent[0] != dst_subresource_extent[0]
3000                && (dst_offset[0] + extent[0]) % dst_subresource_format_block_extent[0] != 0
3001            {
3002                return Err(Box::new(ValidationError {
3003                    problem: format!(
3004                        "`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is not \
3005                        equal to the extent of the subresource of `dst_image` \
3006                        selected by `regions[{0}].dst_subresource`, but \
3007                        it is also not a multiple of coordinate 0 of the block extent of the \
3008                        format of that subresource",
3009                        region_index,
3010                    )
3011                    .into(),
3012                    vuids: &["VUID-VkCopyImageInfo2-dstImage-01732"],
3013                    ..Default::default()
3014                }));
3015            }
3016
3017            if dst_offset[1] + extent[1] != dst_subresource_extent[1]
3018                && (dst_offset[1] + extent[1]) % dst_subresource_format_block_extent[1] != 0
3019            {
3020                return Err(Box::new(ValidationError {
3021                    problem: format!(
3022                        "`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is not \
3023                        equal to the extent of the subresource of `dst_image` \
3024                        selected by `regions[{0}].dst_subresource`, but \
3025                        it is also not a multiple of coordinate 1 of the block extent of the \
3026                        format of that subresource",
3027                        region_index,
3028                    )
3029                    .into(),
3030                    vuids: &["VUID-VkCopyImageInfo2-dstImage-01733"],
3031                    ..Default::default()
3032                }));
3033            }
3034
3035            if dst_offset[2] + extent[2] != dst_subresource_extent[2]
3036                && (dst_offset[2] + extent[2]) % dst_subresource_format_block_extent[2] != 0
3037            {
3038                return Err(Box::new(ValidationError {
3039                    problem: format!(
3040                        "`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is not \
3041                        equal to the extent of the subresource of `dst_image` \
3042                        selected by `regions[{0}].dst_subresource`, but \
3043                        it is also not a multiple of coordinate 2 of the block extent of the \
3044                        format of that subresource",
3045                        region_index,
3046                    )
3047                    .into(),
3048                    vuids: &["VUID-VkCopyImageInfo2-dstImage-01734"],
3049                    ..Default::default()
3050                }));
3051            }
3052
3053            if !(dst_subresource.aspects - ImageAspects::STENCIL).is_empty()
3054                && !dst_image.usage().intersects(ImageUsage::TRANSFER_DST)
3055            {
3056                return Err(Box::new(ValidationError {
3057                    problem: format!(
3058                        "`regions[{0}].dst_subresource.aspects` contains aspects other than \
3059                        `ImageAspects::STENCIL`, but \
3060                        `dst_image.usage()` does not contain `ImageUsage::TRANSFER_DST`",
3061                        region_index,
3062                    )
3063                    .into(),
3064                    vuids: &["VUID-VkCopyImageInfo2-aspect-06663"],
3065                    ..Default::default()
3066                }));
3067            }
3068
3069            if dst_subresource.aspects.intersects(ImageAspects::STENCIL)
3070                && !dst_image
3071                    .stencil_usage()
3072                    .unwrap_or(dst_image.usage())
3073                    .intersects(ImageUsage::TRANSFER_DST)
3074            {
3075                return Err(Box::new(ValidationError {
3076                    problem: format!(
3077                        "`regions[{0}].dst_subresource.aspects` contains \
3078                        `ImageAspects::STENCIL`, but \
3079                        `dst_image.stencil_usage()` does not contain `ImageUsage::TRANSFER_DST`",
3080                        region_index,
3081                    )
3082                    .into(),
3083                    vuids: &["VUID-VkCopyImageInfo2-aspect-06665"],
3084                    ..Default::default()
3085                }));
3086            }
3087
3088            /*
3089                Check src and dst together
3090            */
3091
3092            match (
3093                src_image_format_planes.is_empty(),
3094                dst_image_format_planes.is_empty(),
3095            ) {
3096                (true, true) => {
3097                    if src_subresource.aspects != dst_subresource.aspects {
3098                        return Err(Box::new(ValidationError {
3099                            problem: format!(
3100                                "`src_image.format()` and `dst_image.format()` are both not \
3101                                multi-planar formats, but \
3102                                `regions[{0}].src_subresource.aspects` does not equal \
3103                                `regions[{0}].dst_subresource.aspects`",
3104                                region_index
3105                            )
3106                            .into(),
3107                            vuids: &["VUID-VkCopyImageInfo2-srcImage-01551"],
3108                            ..Default::default()
3109                        }));
3110                    }
3111
3112                    if src_image_format.block_size() != dst_image_format.block_size() {
3113                        return Err(Box::new(ValidationError {
3114                            problem: "`src_image.format()` and `dst_image.format()` are both not \
3115                                multi-planar formats, but \
3116                                `src_image.format().block_size()` does not equal \
3117                                `dst_image.format().block_size()`"
3118                                .into(),
3119                            vuids: &["VUID-VkCopyImageInfo2-srcImage-01548"],
3120                            ..Default::default()
3121                        }));
3122                    }
3123                }
3124                (false, true) => {
3125                    if dst_subresource.aspects != ImageAspects::COLOR {
3126                        return Err(Box::new(ValidationError {
3127                            problem: format!(
3128                                "`src_image.format()` is a multi-planar format, and \
3129                                `dst_image.format()` is not a multi-planar format, but \
3130                                `regions[{}].dst_subresource.aspects` is not \
3131                                `ImageAspects::COLOR`",
3132                                region_index
3133                            )
3134                            .into(),
3135                            vuids: &["VUID-VkCopyImageInfo2-srcImage-01556"],
3136                            ..Default::default()
3137                        }));
3138                    }
3139
3140                    if src_subresource_format.block_size() != dst_image_format.block_size() {
3141                        return Err(Box::new(ValidationError {
3142                            problem: format!(
3143                                "`src_image.format()` is a multi-planar format, and \
3144                                `dst_image.format()` is not a multi-planar format, but \
3145                                the block size of the plane of `src_image.format()` selected by \
3146                                `regions[{}].src_subresource.aspects` does not equal \
3147                                `dst_image.format().block_size()`",
3148                                region_index
3149                            )
3150                            .into(),
3151                            vuids: &["VUID-VkCopyImageInfo2-None-01549"],
3152                            ..Default::default()
3153                        }));
3154                    }
3155                }
3156                (true, false) => {
3157                    if src_subresource.aspects != ImageAspects::COLOR {
3158                        return Err(Box::new(ValidationError {
3159                            problem: format!(
3160                                "`src_image.format()` is not a multi-planar format, and \
3161                                `dst_image.format()` is a multi-planar format, but \
3162                                `regions[{}].src_subresource.aspects` is not \
3163                                `ImageAspects::COLOR`",
3164                                region_index
3165                            )
3166                            .into(),
3167                            vuids: &["VUID-VkCopyImageInfo2-dstImage-01557"],
3168                            ..Default::default()
3169                        }));
3170                    }
3171
3172                    if src_image_format.block_size() != dst_subresource_format.block_size() {
3173                        return Err(Box::new(ValidationError {
3174                            problem: format!(
3175                                "`src_image.format()` is not a multi-planar format, and \
3176                                `dst_image.format()` is a multi-planar format, but \
3177                                `src_image.format().block_size()` does not equal \
3178                                the block size of the plane of `dst_image.format()` selected by \
3179                                `regions[{}].dst_subresource.aspects`",
3180                                region_index
3181                            )
3182                            .into(),
3183                            vuids: &["VUID-VkCopyImageInfo2-None-01549"],
3184                            ..Default::default()
3185                        }));
3186                    }
3187                }
3188                (false, false) => {
3189                    if src_subresource_format.block_size() != dst_subresource_format.block_size() {
3190                        return Err(Box::new(ValidationError {
3191                            problem: format!(
3192                                "`src_image.format()` and `dst_image.format()` are both \
3193                                multi-planar formats, but \
3194                                the block size of the plane of `src_image.format()` selected by \
3195                                `regions[{0}].src_subresource.aspects` does not equal \
3196                                the block size of the plane of `dst_image.format()` selected by \
3197                                `regions[{0}].dst_subresource.aspects`",
3198                                region_index
3199                            )
3200                            .into(),
3201                            vuids: &["VUID-VkCopyImageInfo2-None-01549"],
3202                            ..Default::default()
3203                        }));
3204                    }
3205                }
3206            }
3207
3208            if src_image.image_type() == dst_image.image_type() {
3209                if src_subresource.array_layers.len() != dst_subresource.array_layers.len() {
3210                    return Err(Box::new(ValidationError {
3211                        problem: format!(
3212                            "`src_image.image_type()` equals `dst_image.image_type()`, but \
3213                            the length of `regions[{0}].src_subresource.array_layers` \
3214                            does not equal \
3215                            the length of `regions[{0}].dst_subresource.array_layers`",
3216                            region_index,
3217                        )
3218                        .into(),
3219                        vuids: &["VUID-VkCopyImageInfo2-srcImage-07744"],
3220                        ..Default::default()
3221                    }));
3222                }
3223            }
3224
3225            match (src_image.image_type(), dst_image.image_type()) {
3226                (ImageType::Dim2d, ImageType::Dim2d) => {
3227                    if extent[2] != 1 {
3228                        return Err(Box::new(ValidationError {
3229                            problem: format!(
3230                                "`src_image.image_type()` and `dst_image.image_type()` are \
3231                                both `ImageType::Dim2d`, but `regions[{}].extent[2]` is not 1",
3232                                region_index,
3233                            )
3234                            .into(),
3235                            vuids: &[
3236                                "VUID-VkCopyImageInfo2-srcImage-01790",
3237                                "VUID-VkCopyImageInfo2-apiVersion-08969",
3238                            ],
3239                            ..Default::default()
3240                        }));
3241                    }
3242                }
3243                (ImageType::Dim2d, ImageType::Dim3d) => {
3244                    if extent[2] as usize != src_subresource.array_layers.len() {
3245                        return Err(Box::new(ValidationError {
3246                            problem: format!(
3247                                "`src_image.image_type()` is `ImageType::Dim2d` and \
3248                                `dst_image.image_type()` is `ImageType::Dim3d`, but \
3249                                `regions[{0}].extent[2]` does not equal the length of \
3250                                `regions[{0}].src_subresource.array_layers`",
3251                                region_index,
3252                            )
3253                            .into(),
3254                            vuids: &["VUID-VkCopyImageInfo2-srcImage-01791"],
3255                            ..Default::default()
3256                        }));
3257                    }
3258                }
3259                (ImageType::Dim3d, ImageType::Dim2d) => {
3260                    if extent[2] as usize != dst_subresource.array_layers.len() {
3261                        return Err(Box::new(ValidationError {
3262                            problem: format!(
3263                                "`src_image.image_type()` is `ImageType::Dim3d` and \
3264                                `dst_image.image_type()` is `ImageType::Dim2d`, but \
3265                                `regions[{0}].extent[2]` does not equal the length of \
3266                                `regions[{0}].dst_subresource.array_layers`",
3267                                region_index,
3268                            )
3269                            .into(),
3270                            vuids: &["VUID-VkCopyImageInfo2-dstImage-01792"],
3271                            ..Default::default()
3272                        }));
3273                    }
3274                }
3275                _ => (),
3276            }
3277
3278            // VUID-VkCopyImageInfo2-pRegions-00124
3279            if is_same_image {
3280                let src_region_index = region_index;
3281                let src_subresource_axes = [
3282                    src_subresource.mip_level..src_subresource.mip_level + 1,
3283                    src_subresource.array_layers.start..src_subresource.array_layers.end,
3284                ];
3285                let src_extent_axes = [
3286                    src_offset[0]..src_offset[0] + extent[0],
3287                    src_offset[1]..src_offset[1] + extent[1],
3288                    src_offset[2]..src_offset[2] + extent[2],
3289                ];
3290
3291                for (dst_region_index, dst_region) in regions.iter().enumerate() {
3292                    let &ImageCopy {
3293                        ref dst_subresource,
3294                        dst_offset,
3295                        ..
3296                    } = dst_region;
3297
3298                    // For a single-plane image, the aspects must always be identical anyway
3299                    if src_image_format_aspects.intersects(ImageAspects::PLANE_0)
3300                        && src_subresource.aspects != dst_subresource.aspects
3301                    {
3302                        continue;
3303                    }
3304
3305                    let dst_subresource_axes = [
3306                        dst_subresource.mip_level..dst_subresource.mip_level + 1,
3307                        src_subresource.array_layers.start..src_subresource.array_layers.end,
3308                    ];
3309
3310                    if src_subresource_axes.iter().zip(dst_subresource_axes).any(
3311                        |(src_range, dst_range)| {
3312                            src_range.start >= dst_range.end || dst_range.start >= src_range.end
3313                        },
3314                    ) {
3315                        continue;
3316                    }
3317
3318                    // If the subresource axes all overlap, then the source and destination must
3319                    // have the same layout.
3320                    overlap_subresource_indices = Some((src_region_index, dst_region_index));
3321
3322                    let dst_extent_axes = [
3323                        dst_offset[0]..dst_offset[0] + extent[0],
3324                        dst_offset[1]..dst_offset[1] + extent[1],
3325                        dst_offset[2]..dst_offset[2] + extent[2],
3326                    ];
3327
3328                    // There is only overlap if all of the axes overlap.
3329                    if src_extent_axes
3330                        .iter()
3331                        .zip(dst_extent_axes)
3332                        .any(|(src_range, dst_range)| {
3333                            src_range.start >= dst_range.end || dst_range.start >= src_range.end
3334                        })
3335                    {
3336                        continue;
3337                    }
3338
3339                    overlap_extent_indices = Some((src_region_index, dst_region_index));
3340                }
3341            }
3342        }
3343
3344        if let Some((src_region_index, dst_region_index)) = overlap_extent_indices {
3345            return Err(Box::new(ValidationError {
3346                problem: format!(
3347                    "`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \
3348                    overlaps with `regions[{1}].dst_subresource`, but \
3349                    the `src_offset` and `extent` of `regions[{0}]` overlaps with \
3350                    the `dst_offset` and `extent` of `regions[{1}]`",
3351                    src_region_index, dst_region_index
3352                )
3353                .into(),
3354                vuids: &["VUID-VkCopyImageInfo2-pRegions-00124"],
3355                ..Default::default()
3356            }));
3357        }
3358
3359        if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices {
3360            if src_image_layout != dst_image_layout {
3361                return Err(Box::new(ValidationError {
3362                    problem: format!(
3363                        "`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \
3364                        overlaps with `regions[{1}].dst_subresource`, but \
3365                        `src_image_layout` does not equal `dst_image_layout`",
3366                        src_region_index, dst_region_index
3367                    )
3368                    .into(),
3369                    vuids: &[
3370                        "VUID-VkCopyImageInfo2-srcImageLayout-00128",
3371                        "VUID-VkCopyImageInfo2-dstImageLayout-00133",
3372                    ],
3373                    ..Default::default()
3374                }));
3375            }
3376        }
3377
3378        Ok(())
3379    }
3380
3381    pub(crate) fn to_vk2<'a>(
3382        &self,
3383        regions_vk: &'a [ash::vk::ImageCopy2<'static>],
3384    ) -> ash::vk::CopyImageInfo2<'a> {
3385        let &Self {
3386            ref src_image,
3387            src_image_layout,
3388            ref dst_image,
3389            dst_image_layout,
3390            regions: _,
3391            _ne: _,
3392        } = self;
3393
3394        ash::vk::CopyImageInfo2::default()
3395            .src_image(src_image.handle())
3396            .src_image_layout(src_image_layout.into())
3397            .dst_image(dst_image.handle())
3398            .dst_image_layout(dst_image_layout.into())
3399            .regions(regions_vk)
3400    }
3401
3402    pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::ImageCopy2<'static>; 8]> {
3403        self.regions.iter().map(ImageCopy::to_vk2).collect()
3404    }
3405
3406    pub(crate) fn to_vk(&self) -> CopyImageInfoVk {
3407        let &Self {
3408            ref src_image,
3409            src_image_layout,
3410            ref dst_image,
3411            dst_image_layout,
3412            regions: _,
3413            _ne: _,
3414        } = self;
3415
3416        CopyImageInfoVk {
3417            src_image_vk: src_image.handle(),
3418            src_image_layout_vk: src_image_layout.into(),
3419            dst_image_vk: dst_image.handle(),
3420            dst_image_layout_vk: dst_image_layout.into(),
3421        }
3422    }
3423
3424    pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::ImageCopy; 8]> {
3425        self.regions.iter().map(ImageCopy::to_vk).collect()
3426    }
3427}
3428
3429pub(crate) struct CopyImageInfoVk {
3430    pub(crate) src_image_vk: ash::vk::Image,
3431    pub(crate) src_image_layout_vk: ash::vk::ImageLayout,
3432    pub(crate) dst_image_vk: ash::vk::Image,
3433    pub(crate) dst_image_layout_vk: ash::vk::ImageLayout,
3434}
3435
3436/// A region of data to copy between images.
3437#[derive(Clone, Debug)]
3438pub struct ImageCopy {
3439    /// The subresource of `src_image` to copy from.
3440    ///
3441    /// The default value is empty, which must be overridden.
3442    pub src_subresource: ImageSubresourceLayers,
3443
3444    /// The offset from the zero coordinate of `src_image` that copying will start from.
3445    ///
3446    /// The default value is `[0; 3]`.
3447    pub src_offset: [u32; 3],
3448
3449    /// The subresource of `dst_image` to copy to.
3450    ///
3451    /// The default value is empty, which must be overridden.
3452    pub dst_subresource: ImageSubresourceLayers,
3453
3454    /// The offset from the zero coordinate of `dst_image` that copying will start from.
3455    ///
3456    /// The default value is `[0; 3]`.
3457    pub dst_offset: [u32; 3],
3458
3459    /// The extent of texels to copy.
3460    ///
3461    /// The default value is `[0; 3]`, which must be overridden.
3462    pub extent: [u32; 3],
3463
3464    pub _ne: crate::NonExhaustive,
3465}
3466
3467impl Default for ImageCopy {
3468    #[inline]
3469    fn default() -> Self {
3470        Self {
3471            src_subresource: ImageSubresourceLayers {
3472                aspects: ImageAspects::empty(),
3473                mip_level: 0,
3474                array_layers: 0..0,
3475            },
3476            src_offset: [0; 3],
3477            dst_subresource: ImageSubresourceLayers {
3478                aspects: ImageAspects::empty(),
3479                mip_level: 0,
3480                array_layers: 0..0,
3481            },
3482            dst_offset: [0; 3],
3483            extent: [0; 3],
3484            _ne: crate::NonExhaustive(()),
3485        }
3486    }
3487}
3488
3489impl ImageCopy {
3490    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
3491        let &Self {
3492            ref src_subresource,
3493            src_offset: _,
3494            ref dst_subresource,
3495            dst_offset: _,
3496            extent,
3497            _ne,
3498        } = self;
3499
3500        src_subresource
3501            .validate(device)
3502            .map_err(|err| err.add_context("src_subresource"))?;
3503
3504        dst_subresource
3505            .validate(device)
3506            .map_err(|err| err.add_context("dst_subresource"))?;
3507
3508        if device.api_version() < Version::V1_1 {
3509            if src_subresource.aspects != dst_subresource.aspects
3510                && !device.enabled_extensions().khr_sampler_ycbcr_conversion
3511            {
3512                return Err(Box::new(ValidationError {
3513                    problem: "`src_subresource.aspects` does not equal `dst_subresource.aspects`"
3514                        .into(),
3515                    vuids: &["VUID-VkImageCopy2-apiVersion-07940"],
3516                    ..Default::default()
3517                }));
3518            }
3519
3520            if src_subresource.array_layers.len() != dst_subresource.array_layers.len()
3521                && !device.enabled_extensions().khr_maintenance1
3522            {
3523                return Err(Box::new(ValidationError {
3524                    problem: "the length of `src_subresource.array_layers` does not equal \
3525                        the length of `dst_subresource.array_layers`"
3526                        .into(),
3527                    vuids: &["VUID-VkImageCopy2-extent-00140"],
3528                    ..Default::default()
3529                }));
3530            }
3531        }
3532
3533        if extent[0] == 0 {
3534            return Err(Box::new(ValidationError {
3535                context: "extent[0]".into(),
3536                problem: "is zero".into(),
3537                vuids: &["VUID-VkImageCopy2-extent-06668"],
3538                ..Default::default()
3539            }));
3540        }
3541
3542        if extent[1] == 0 {
3543            return Err(Box::new(ValidationError {
3544                context: "extent[1]".into(),
3545                problem: "is zero".into(),
3546                vuids: &["VUID-VkImageCopy2-extent-06669"],
3547                ..Default::default()
3548            }));
3549        }
3550
3551        if extent[2] == 0 {
3552            return Err(Box::new(ValidationError {
3553                context: "extent[2]".into(),
3554                problem: "is zero".into(),
3555                vuids: &["VUID-VkImageCopy2-extent-06670"],
3556                ..Default::default()
3557            }));
3558        }
3559
3560        Ok(())
3561    }
3562
3563    pub(crate) fn to_vk2(&self) -> ash::vk::ImageCopy2<'static> {
3564        let &Self {
3565            ref src_subresource,
3566            src_offset,
3567            ref dst_subresource,
3568            dst_offset,
3569            extent,
3570            _ne: _,
3571        } = self;
3572
3573        ash::vk::ImageCopy2::default()
3574            .src_subresource(src_subresource.to_vk())
3575            .src_offset(ash::vk::Offset3D {
3576                x: src_offset[0] as i32,
3577                y: src_offset[1] as i32,
3578                z: src_offset[2] as i32,
3579            })
3580            .dst_subresource(dst_subresource.to_vk())
3581            .dst_offset(ash::vk::Offset3D {
3582                x: dst_offset[0] as i32,
3583                y: dst_offset[1] as i32,
3584                z: dst_offset[2] as i32,
3585            })
3586            .extent(ash::vk::Extent3D {
3587                width: extent[0],
3588                height: extent[1],
3589                depth: extent[2],
3590            })
3591    }
3592
3593    pub(crate) fn to_vk(&self) -> ash::vk::ImageCopy {
3594        let &Self {
3595            ref src_subresource,
3596            src_offset,
3597            ref dst_subresource,
3598            dst_offset,
3599            extent,
3600            _ne: _,
3601        } = self;
3602
3603        ash::vk::ImageCopy {
3604            src_subresource: src_subresource.to_vk(),
3605            src_offset: ash::vk::Offset3D {
3606                x: src_offset[0] as i32,
3607                y: src_offset[1] as i32,
3608                z: src_offset[2] as i32,
3609            },
3610            dst_subresource: dst_subresource.to_vk(),
3611            dst_offset: ash::vk::Offset3D {
3612                x: dst_offset[0] as i32,
3613                y: dst_offset[1] as i32,
3614                z: dst_offset[2] as i32,
3615            },
3616            extent: ash::vk::Extent3D {
3617                width: extent[0],
3618                height: extent[1],
3619                depth: extent[2],
3620            },
3621        }
3622    }
3623}
3624
3625/// Parameters to copy data from a buffer to an image.
3626#[derive(Clone, Debug)]
3627pub struct CopyBufferToImageInfo {
3628    /// The buffer to copy from.
3629    ///
3630    /// There is no default value.
3631    pub src_buffer: Subbuffer<[u8]>,
3632
3633    /// The image to copy to.
3634    ///
3635    /// There is no default value.
3636    pub dst_image: Arc<Image>,
3637
3638    /// The layout used for `dst_image` during the copy operation.
3639    ///
3640    /// The following layouts are allowed:
3641    /// - [`ImageLayout::TransferDstOptimal`]
3642    /// - [`ImageLayout::General`]
3643    ///
3644    /// The default value is [`ImageLayout::TransferDstOptimal`].
3645    pub dst_image_layout: ImageLayout,
3646
3647    /// The regions of the buffer and image to copy between.
3648    ///
3649    /// The default value is a single region, covering all of the buffer and the first mip level of
3650    /// the image. All aspects of the image are selected, or `plane0` if the image is multi-planar.
3651    pub regions: SmallVec<[BufferImageCopy; 1]>,
3652
3653    pub _ne: crate::NonExhaustive,
3654}
3655
3656impl CopyBufferToImageInfo {
3657    /// Returns a `CopyBufferToImageInfo` with the specified `src_buffer` and
3658    /// `dst_image`.
3659    #[inline]
3660    pub fn buffer_image(src_buffer: Subbuffer<impl ?Sized>, dst_image: Arc<Image>) -> Self {
3661        let region = BufferImageCopy {
3662            image_subresource: dst_image.subresource_layers(),
3663            image_extent: dst_image.extent(),
3664            ..Default::default()
3665        };
3666
3667        Self {
3668            src_buffer: src_buffer.into_bytes(),
3669            dst_image,
3670            dst_image_layout: ImageLayout::TransferDstOptimal,
3671            regions: smallvec![region],
3672            _ne: crate::NonExhaustive(()),
3673        }
3674    }
3675
3676    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
3677        let &Self {
3678            ref src_buffer,
3679            ref dst_image,
3680            dst_image_layout,
3681            ref regions,
3682            _ne: _,
3683        } = self;
3684
3685        dst_image_layout.validate_device(device).map_err(|err| {
3686            err.add_context("dst_image_layout")
3687                .set_vuids(&["VUID-VkCopyBufferToImageInfo2-dstImageLayout-parameter"])
3688        })?;
3689
3690        // VUID-VkCopyBufferToImageInfo2-commonparent
3691        assert_eq!(device, src_buffer.device().as_ref());
3692        assert_eq!(device, dst_image.device().as_ref());
3693
3694        let dst_image_format = dst_image.format();
3695        let dst_image_format_aspects = dst_image_format.aspects();
3696        let dst_image_format_planes = dst_image_format.planes();
3697        let dst_image_format_subsampled_extent = dst_image_format
3698            .ycbcr_chroma_sampling()
3699            .map_or(dst_image.extent(), |s| {
3700                s.subsampled_extent(dst_image.extent())
3701            });
3702
3703        if !src_buffer
3704            .buffer()
3705            .usage()
3706            .intersects(BufferUsage::TRANSFER_SRC)
3707        {
3708            return Err(Box::new(ValidationError {
3709                context: "src_buffer.buffer().usage()".into(),
3710                problem: "does not contain `BufferUsage::TRANSFER_SRC`".into(),
3711                vuids: &["VUID-VkCopyBufferToImageInfo2-srcBuffer-00174"],
3712                ..Default::default()
3713            }));
3714        }
3715
3716        if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) {
3717            return Err(Box::new(ValidationError {
3718                context: "dst_image.usage()".into(),
3719                problem: "does not contain `ImageUsage::TRANSFER_DST`".into(),
3720                vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-00177"],
3721                ..Default::default()
3722            }));
3723        }
3724
3725        if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
3726            if !dst_image
3727                .format_features()
3728                .intersects(FormatFeatures::TRANSFER_DST)
3729            {
3730                return Err(Box::new(ValidationError {
3731                    context: "dst_image.format_features()".into(),
3732                    problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(),
3733                    vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-01997"],
3734                    ..Default::default()
3735                }));
3736            }
3737        }
3738
3739        if dst_image.samples() != SampleCount::Sample1 {
3740            return Err(Box::new(ValidationError {
3741                context: "dst_image.samples()".into(),
3742                problem: "is not `SampleCount::Sample1`".into(),
3743                vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07973"],
3744                ..Default::default()
3745            }));
3746        }
3747
3748        if !matches!(
3749            dst_image_layout,
3750            ImageLayout::TransferDstOptimal | ImageLayout::General
3751        ) {
3752            return Err(Box::new(ValidationError {
3753                context: "dst_image_layout".into(),
3754                problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
3755                    .into(),
3756                vuids: &["VUID-VkCopyBufferToImageInfo2-dstImageLayout-01396"],
3757                ..Default::default()
3758            }));
3759        }
3760
3761        for (region_index, region) in regions.iter().enumerate() {
3762            region
3763                .validate(device)
3764                .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
3765
3766            let &BufferImageCopy {
3767                buffer_offset,
3768                buffer_row_length,
3769                buffer_image_height,
3770                ref image_subresource,
3771                image_offset,
3772                image_extent,
3773                _ne: _,
3774            } = region;
3775
3776            /*
3777               Check image
3778            */
3779
3780            if image_subresource.mip_level >= dst_image.mip_levels() {
3781                return Err(Box::new(ValidationError {
3782                    problem: format!(
3783                        "`regions[{}].image_subresource.mip_level` is not less than \
3784                        `dst_image.mip_levels()`",
3785                        region_index
3786                    )
3787                    .into(),
3788                    vuids: &["VUID-VkCopyBufferToImageInfo2-imageSubresource-01701"],
3789                    ..Default::default()
3790                }));
3791            }
3792
3793            let mut image_subresource_format = dst_image_format;
3794            let mut image_subresource_extent =
3795                mip_level_extent(dst_image.extent(), image_subresource.mip_level).unwrap();
3796
3797            if dst_image_format_planes.is_empty() {
3798                if !dst_image_format_aspects.contains(image_subresource.aspects) {
3799                    return Err(Box::new(ValidationError {
3800                        problem: format!(
3801                            "`regions[{}].image_subresource.aspects` is not a subset of \
3802                            `dst_image.format().aspects()`",
3803                            region_index
3804                        )
3805                        .into(),
3806                        vuids: &["VUID-VkCopyBufferToImageInfo2-aspectMask-00211"],
3807                        ..Default::default()
3808                    }));
3809                }
3810            } else if dst_image_format_planes.len() == 2 {
3811                match image_subresource.aspects {
3812                    ImageAspects::PLANE_0 => {
3813                        image_subresource_format = dst_image_format_planes[0];
3814                    }
3815                    ImageAspects::PLANE_1 => {
3816                        image_subresource_format = dst_image_format_planes[1];
3817                        image_subresource_extent = dst_image_format_subsampled_extent;
3818                    }
3819                    _ => {
3820                        return Err(Box::new(ValidationError {
3821                            problem: format!(
3822                                "`dst_image.format()` is a multi-planar format with two planes, \
3823                                but `regions[{}].image_subresource.aspect` is not \
3824                                `ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`",
3825                                region_index,
3826                            )
3827                            .into(),
3828                            vuids: &[
3829                                "VUID-VkCopyBufferToImageInfo2-dstImage-07981",
3830                                "VUID-VkCopyBufferToImageInfo2-aspectMask-00211",
3831                            ],
3832                            ..Default::default()
3833                        }));
3834                    }
3835                }
3836            } else if dst_image_format_planes.len() == 3 {
3837                match image_subresource.aspects {
3838                    ImageAspects::PLANE_0 => {
3839                        image_subresource_format = dst_image_format_planes[0];
3840                    }
3841                    ImageAspects::PLANE_1 => {
3842                        image_subresource_format = dst_image_format_planes[1];
3843                        image_subresource_extent = dst_image_format_subsampled_extent;
3844                    }
3845                    ImageAspects::PLANE_2 => {
3846                        image_subresource_format = dst_image_format_planes[2];
3847                        image_subresource_extent = dst_image_format_subsampled_extent;
3848                    }
3849                    _ => {
3850                        return Err(Box::new(ValidationError {
3851                            problem: format!(
3852                                "`dst_image.format()` is a multi-planar format with three planes, \
3853                                but `regions[{}].image_subresource.aspect` is not \
3854                                `ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \
3855                                `ImageAspects::PLANE_2`",
3856                                region_index,
3857                            )
3858                            .into(),
3859                            vuids: &[
3860                                "VUID-VkCopyBufferToImageInfo2-dstImage-07982",
3861                                "VUID-VkCopyBufferToImageInfo2-aspectMask-00211",
3862                            ],
3863                            ..Default::default()
3864                        }));
3865                    }
3866                }
3867            }
3868
3869            match dst_image.image_type() {
3870                ImageType::Dim1d => {
3871                    if image_offset[1] != 0 {
3872                        return Err(Box::new(ValidationError {
3873                            problem: format!(
3874                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
3875                                `regions[{}].image_offset[1]` is not 0",
3876                                region_index,
3877                            )
3878                            .into(),
3879                            vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07979"],
3880                            ..Default::default()
3881                        }));
3882                    }
3883
3884                    if image_extent[1] != 1 {
3885                        return Err(Box::new(ValidationError {
3886                            problem: format!(
3887                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
3888                                `regions[{}].image_extent[1]` is not 1",
3889                                region_index,
3890                            )
3891                            .into(),
3892                            vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07979"],
3893                            ..Default::default()
3894                        }));
3895                    }
3896
3897                    if image_offset[2] != 0 {
3898                        return Err(Box::new(ValidationError {
3899                            problem: format!(
3900                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
3901                                `regions[{}].image_offset[2]` is not 0",
3902                                region_index,
3903                            )
3904                            .into(),
3905                            vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"],
3906                            ..Default::default()
3907                        }));
3908                    }
3909
3910                    if image_extent[2] != 1 {
3911                        return Err(Box::new(ValidationError {
3912                            problem: format!(
3913                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
3914                                `regions[{}].image_extent[2]` is not 1",
3915                                region_index,
3916                            )
3917                            .into(),
3918                            vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"],
3919                            ..Default::default()
3920                        }));
3921                    }
3922                }
3923                ImageType::Dim2d => {
3924                    if image_offset[2] != 0 {
3925                        return Err(Box::new(ValidationError {
3926                            problem: format!(
3927                                "`dst_image.image_type()` is `ImageType::Dim2d`, but \
3928                                `regions[{}].image_offset[2]` is not 0",
3929                                region_index,
3930                            )
3931                            .into(),
3932                            vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"],
3933                            ..Default::default()
3934                        }));
3935                    }
3936
3937                    if image_extent[2] != 1 {
3938                        return Err(Box::new(ValidationError {
3939                            problem: format!(
3940                                "`dst_image.image_type()` is `ImageType::Dim2d`, but \
3941                                `regions[{}].image_extent[2]` is not 1",
3942                                region_index,
3943                            )
3944                            .into(),
3945                            vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"],
3946                            ..Default::default()
3947                        }));
3948                    }
3949                }
3950                ImageType::Dim3d => {
3951                    if image_subresource.array_layers != (0..1) {
3952                        return Err(Box::new(ValidationError {
3953                            problem: format!(
3954                                "`dst_image.image_type()` is  `ImageType::Dim3d`, but \
3955                                `regions[{}].image_subresource.array_layers` is not `0..1`",
3956                                region_index,
3957                            )
3958                            .into(),
3959                            vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07983"],
3960                            ..Default::default()
3961                        }));
3962                    }
3963                }
3964            }
3965
3966            if image_subresource.array_layers.end > dst_image.array_layers() {
3967                return Err(Box::new(ValidationError {
3968                    problem: format!(
3969                        "`regions[{}].dst_subresource.array_layers.end` is not less than \
3970                        `dst_image.array_layers()`",
3971                        region_index
3972                    )
3973                    .into(),
3974                    vuids: &["VUID-VkCopyBufferToImageInfo2-imageSubresource-07968"],
3975                    ..Default::default()
3976                }));
3977            }
3978
3979            if image_offset[0] + image_extent[0] > image_subresource_extent[0] {
3980                return Err(Box::new(ValidationError {
3981                    problem: format!(
3982                        "`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is greater \
3983                        than coordinate 0 of the extent of the subresource of `dst_image` \
3984                        selected by `regions[{0}].image_subresource`",
3985                        region_index,
3986                    )
3987                    .into(),
3988                    vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-06223"],
3989                    ..Default::default()
3990                }));
3991            }
3992
3993            if image_offset[1] + image_extent[1] > image_subresource_extent[1] {
3994                return Err(Box::new(ValidationError {
3995                    problem: format!(
3996                        "`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is greater \
3997                        than coordinate 1 of the extent of the subresource of `dst_image` \
3998                        selected by `regions[{0}].image_subresource`",
3999                        region_index,
4000                    )
4001                    .into(),
4002                    vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-06224"],
4003                    ..Default::default()
4004                }));
4005            }
4006
4007            if image_offset[2] + image_extent[2] > image_subresource_extent[2] {
4008                return Err(Box::new(ValidationError {
4009                    problem: format!(
4010                        "`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is greater \
4011                        than coordinate 2 of the extent of the subresource of `dst_image` \
4012                        selected by `regions[{0}].image_subresource`",
4013                        region_index,
4014                    )
4015                    .into(),
4016                    vuids: &["VUID-VkCopyBufferToImageInfo2-imageOffset-00200"],
4017                    ..Default::default()
4018                }));
4019            }
4020
4021            let image_subresource_format_block_extent = image_subresource_format.block_extent();
4022
4023            if image_offset[0] % image_subresource_format_block_extent[0] != 0 {
4024                return Err(Box::new(ValidationError {
4025                    problem: format!(
4026                        "`regions[{0}].image_offset[0]` is not a multiple of coordinate 0 of the \
4027                        block extent of the format of the subresource of `dst_image` \
4028                        selected by `regions[{0}].image_subresource`",
4029                        region_index,
4030                    )
4031                    .into(),
4032                    vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07274"],
4033                    ..Default::default()
4034                }));
4035            }
4036
4037            if image_offset[1] % image_subresource_format_block_extent[1] != 0 {
4038                return Err(Box::new(ValidationError {
4039                    problem: format!(
4040                        "`regions[{0}].image_offset[1]` is not a multiple of coordinate 1 of the \
4041                        block extent of the format of the subresource of `dst_image` \
4042                        selected by `regions[{0}].image_subresource`",
4043                        region_index,
4044                    )
4045                    .into(),
4046                    vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07275"],
4047                    ..Default::default()
4048                }));
4049            }
4050
4051            if image_offset[2] % image_subresource_format_block_extent[2] != 0 {
4052                return Err(Box::new(ValidationError {
4053                    problem: format!(
4054                        "`regions[{0}].image_offset[2]` is not a multiple of coordinate 2 of the \
4055                        block extent of the format of the subresource of `dst_image` \
4056                        selected by `regions[{0}].image_subresource`",
4057                        region_index,
4058                    )
4059                    .into(),
4060                    vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07276"],
4061                    ..Default::default()
4062                }));
4063            }
4064
4065            if image_offset[0] + image_extent[0] != image_subresource_extent[0]
4066                && (image_offset[0] + image_extent[0]) % image_subresource_format_block_extent[0]
4067                    != 0
4068            {
4069                return Err(Box::new(ValidationError {
4070                    problem: format!(
4071                        "`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is not \
4072                        equal to the extent of the subresource of `dst_image` \
4073                        selected by `regions[{0}].image_subresource`, but \
4074                        it is also not a multiple of coordinate 0 of the block extent of the \
4075                        format of that subresource",
4076                        region_index,
4077                    )
4078                    .into(),
4079                    vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00207"],
4080                    ..Default::default()
4081                }));
4082            }
4083
4084            if image_offset[1] + image_extent[1] != image_subresource_extent[1]
4085                && (image_offset[1] + image_extent[1]) % image_subresource_format_block_extent[1]
4086                    != 0
4087            {
4088                return Err(Box::new(ValidationError {
4089                    problem: format!(
4090                        "`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is not \
4091                        equal to the extent of the subresource of `dst_image` \
4092                        selected by `regions[{0}].image_subresource`, but \
4093                        it is also not a multiple of coordinate 1 of the block extent of the \
4094                        format of that subresource",
4095                        region_index,
4096                    )
4097                    .into(),
4098                    vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00208"],
4099                    ..Default::default()
4100                }));
4101            }
4102
4103            if image_offset[2] + image_extent[2] != image_subresource_extent[2]
4104                && (image_offset[2] + image_extent[2]) % image_subresource_format_block_extent[2]
4105                    != 0
4106            {
4107                return Err(Box::new(ValidationError {
4108                    problem: format!(
4109                        "`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is not \
4110                        equal to the extent of the subresource of `dst_image` \
4111                        selected by `regions[{0}].image_subresource`, but \
4112                        it is also not a multiple of coordinate 2 of the block extent of the \
4113                        format of that subresource",
4114                        region_index,
4115                    )
4116                    .into(),
4117                    vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00209"],
4118                    ..Default::default()
4119                }));
4120            }
4121
4122            /*
4123               Check buffer and image together
4124            */
4125
4126            let image_subresource_format_block_size = image_subresource_format.block_size();
4127
4128            if dst_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
4129                if (src_buffer.offset() + buffer_offset) % 4 != 0 {
4130                    return Err(Box::new(ValidationError {
4131                        problem: format!(
4132                            "`dst_image.format()` is a depth/stencil format, but \
4133                            `src_buffer.offset() + regions[{0}].buffer_offset` is not a \
4134                            multiple of 4",
4135                            region_index,
4136                        )
4137                        .into(),
4138                        vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07978"],
4139                        ..Default::default()
4140                    }));
4141                }
4142            } else {
4143                if (src_buffer.offset() + buffer_offset) % image_subresource_format_block_size != 0
4144                {
4145                    return Err(Box::new(ValidationError {
4146                        problem: format!(
4147                            "`dst_image.format()` is not a depth/stencil format, but \
4148                            `src_buffer.offset() + regions[{0}].buffer_offset` is not a \
4149                            multiple of the block size of the format of the subresource of \
4150                            `dst_image` selected by `regions[{0}].image_subresource`",
4151                            region_index,
4152                        )
4153                        .into(),
4154                        vuids: &[
4155                            "VUID-VkCopyBufferToImageInfo2-dstImage-07975",
4156                            "VUID-VkCopyBufferToImageInfo2-dstImage-07976",
4157                        ],
4158                        ..Default::default()
4159                    }));
4160                }
4161            }
4162
4163            if buffer_row_length % image_subresource_format_block_extent[0] != 0 {
4164                return Err(Box::new(ValidationError {
4165                    problem: format!(
4166                        "`regions[{0}].buffer_row_length` is not a multiple of coordinate 0 of \
4167                        the block extent of the format of the subresource of `dst_image` \
4168                        selected by `regions[{0}].image_subresource`",
4169                        region_index,
4170                    )
4171                    .into(),
4172                    vuids: &["VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203"],
4173                    ..Default::default()
4174                }));
4175            }
4176
4177            if buffer_image_height % image_subresource_format_block_extent[1] != 0 {
4178                return Err(Box::new(ValidationError {
4179                    problem: format!(
4180                        "`regions[{0}].buffer_image_height` is not a multiple of coordinate 1 of \
4181                        the block extent of the format of the subresource of `dst_image` \
4182                        selected by `regions[{0}].image_subresource`",
4183                        region_index,
4184                    )
4185                    .into(),
4186                    vuids: &["VUID-VkCopyBufferToImageInfo2-bufferImageHeight-00204"],
4187                    ..Default::default()
4188                }));
4189            }
4190
4191            if (buffer_row_length / image_subresource_format_block_extent[0]) as DeviceSize
4192                * image_subresource_format_block_size
4193                > 0x7FFFFFFF
4194            {
4195                return Err(Box::new(ValidationError {
4196                    problem: format!(
4197                        "`regions[{0}].buffer_row_length`, divided by the block size of the \
4198                        format of the subresource of `dst_image` selected by \
4199                        `regions[{0}].image_subresource`, and then multiplied by the block size \
4200                        of that subresource, is greater than 0x7FFFFFFF",
4201                        region_index,
4202                    )
4203                    .into(),
4204                    vuids: &["VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203"],
4205                    ..Default::default()
4206                }));
4207            }
4208
4209            if buffer_offset + region.buffer_copy_size(image_subresource_format) > src_buffer.size()
4210            {
4211                return Err(Box::new(ValidationError {
4212                    problem: format!(
4213                        "`regions[{0}].buffer_offset` plus the number of bytes being copied \
4214                        is greater than `src_buffer.size()`",
4215                        region_index,
4216                    )
4217                    .into(),
4218                    vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-00171"],
4219                    ..Default::default()
4220                }));
4221            }
4222        }
4223
4224        // VUID-VkCopyBufferToImageInfo2-pRegions-00173
4225        // Can't occur as long as memory aliasing isn't allowed.
4226
4227        // VUID-VkCopyBufferToImageInfo2-pRegions-07931
4228        // Unsafe, can't validate
4229
4230        Ok(())
4231    }
4232
4233    pub(crate) fn to_vk2<'a>(
4234        &self,
4235        regions_vk: &'a [ash::vk::BufferImageCopy2<'static>],
4236    ) -> ash::vk::CopyBufferToImageInfo2<'a> {
4237        let &Self {
4238            ref src_buffer,
4239            ref dst_image,
4240            dst_image_layout,
4241            regions: _,
4242            _ne: _,
4243        } = self;
4244
4245        ash::vk::CopyBufferToImageInfo2::default()
4246            .src_buffer(src_buffer.buffer().handle())
4247            .dst_image(dst_image.handle())
4248            .dst_image_layout(dst_image_layout.into())
4249            .regions(regions_vk)
4250    }
4251
4252    pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::BufferImageCopy2<'static>; 8]> {
4253        let Self {
4254            src_buffer,
4255            regions,
4256            ..
4257        } = self;
4258
4259        regions
4260            .iter()
4261            .map(|region| {
4262                let mut region_vk = region.to_vk2();
4263                region_vk.buffer_offset += src_buffer.offset();
4264                region_vk
4265            })
4266            .collect()
4267    }
4268
4269    pub(crate) fn to_vk(&self) -> CopyBufferToImageInfoVk {
4270        let &Self {
4271            ref src_buffer,
4272            ref dst_image,
4273            dst_image_layout,
4274            regions: _,
4275            _ne: _,
4276        } = self;
4277
4278        CopyBufferToImageInfoVk {
4279            src_buffer_vk: src_buffer.buffer().handle(),
4280            dst_image_vk: dst_image.handle(),
4281            dst_image_layout_vk: dst_image_layout.into(),
4282        }
4283    }
4284
4285    pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::BufferImageCopy; 8]> {
4286        let Self {
4287            src_buffer,
4288            regions,
4289            ..
4290        } = self;
4291
4292        regions
4293            .iter()
4294            .map(|region| {
4295                let mut region_vk = region.to_vk();
4296                region_vk.buffer_offset += src_buffer.offset();
4297                region_vk
4298            })
4299            .collect()
4300    }
4301}
4302
4303pub(crate) struct CopyBufferToImageInfoVk {
4304    pub(crate) src_buffer_vk: ash::vk::Buffer,
4305    pub(crate) dst_image_vk: ash::vk::Image,
4306    pub(crate) dst_image_layout_vk: ash::vk::ImageLayout,
4307}
4308
4309/// Parameters to copy data from an image to a buffer.
4310#[derive(Clone, Debug)]
4311pub struct CopyImageToBufferInfo {
4312    /// The image to copy from.
4313    ///
4314    /// There is no default value.
4315    pub src_image: Arc<Image>,
4316
4317    /// The layout used for `src_image` during the copy operation.
4318    ///
4319    /// The following layouts are allowed:
4320    /// - [`ImageLayout::TransferSrcOptimal`]
4321    /// - [`ImageLayout::General`]
4322    ///
4323    /// The default value is [`ImageLayout::TransferSrcOptimal`].
4324    pub src_image_layout: ImageLayout,
4325
4326    /// The buffer to copy to.
4327    ///
4328    /// There is no default value.
4329    pub dst_buffer: Subbuffer<[u8]>,
4330
4331    /// The regions of the image and buffer to copy between.
4332    ///
4333    /// The default value is a single region, covering all of the buffer and the first mip level of
4334    /// the image. All aspects of the image are selected, or `plane0` if the image is multi-planar.
4335    pub regions: SmallVec<[BufferImageCopy; 1]>,
4336
4337    pub _ne: crate::NonExhaustive,
4338}
4339
4340impl CopyImageToBufferInfo {
4341    /// Returns a `CopyImageToBufferInfo` with the specified `src_image` and
4342    /// `dst_buffer`.
4343    #[inline]
4344    pub fn image_buffer(src_image: Arc<Image>, dst_buffer: Subbuffer<impl ?Sized>) -> Self {
4345        let region = BufferImageCopy {
4346            image_subresource: src_image.subresource_layers(),
4347            image_extent: src_image.extent(),
4348            ..Default::default()
4349        };
4350
4351        Self {
4352            src_image,
4353            src_image_layout: ImageLayout::TransferSrcOptimal,
4354            dst_buffer: dst_buffer.into_bytes(),
4355            regions: smallvec![region],
4356            _ne: crate::NonExhaustive(()),
4357        }
4358    }
4359
4360    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
4361        let &Self {
4362            ref src_image,
4363            src_image_layout,
4364            ref dst_buffer,
4365            ref regions,
4366            _ne: _,
4367        } = self;
4368
4369        src_image_layout.validate_device(device).map_err(|err| {
4370            err.add_context("src_image_layout")
4371                .set_vuids(&["VUID-VkCopyImageToBufferInfo2-srcImageLayout-parameter"])
4372        })?;
4373
4374        // VUID-VkCopyImageToBufferInfo2-commonparent
4375        assert_eq!(device, src_image.device().as_ref());
4376        assert_eq!(device, dst_buffer.device().as_ref());
4377
4378        let src_image_format = src_image.format();
4379        let src_image_format_aspects = src_image_format.aspects();
4380        let src_image_format_planes = src_image_format.planes();
4381        let src_image_format_subsampled_extent = src_image_format
4382            .ycbcr_chroma_sampling()
4383            .map_or(src_image.extent(), |s| {
4384                s.subsampled_extent(src_image.extent())
4385            });
4386
4387        if !dst_buffer
4388            .buffer()
4389            .usage()
4390            .intersects(BufferUsage::TRANSFER_DST)
4391        {
4392            return Err(Box::new(ValidationError {
4393                context: "dst_buffer.buffer().usage()".into(),
4394                problem: "does not contain `BufferUsage::TRANSFER_DST`".into(),
4395                vuids: &["VUID-VkCopyImageToBufferInfo2-dstBuffer-00191"],
4396                ..Default::default()
4397            }));
4398        }
4399
4400        if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) {
4401            return Err(Box::new(ValidationError {
4402                context: "src_image.usage()".into(),
4403                problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(),
4404                vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-00186"],
4405                ..Default::default()
4406            }));
4407        }
4408
4409        if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
4410            if !src_image
4411                .format_features()
4412                .intersects(FormatFeatures::TRANSFER_SRC)
4413            {
4414                return Err(Box::new(ValidationError {
4415                    context: "src_image.format_features()".into(),
4416                    problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(),
4417                    vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-01998"],
4418                    ..Default::default()
4419                }));
4420            }
4421        }
4422
4423        if src_image.samples() != SampleCount::Sample1 {
4424            return Err(Box::new(ValidationError {
4425                context: "src_image.samples()".into(),
4426                problem: "is not `SampleCount::Sample1`".into(),
4427                vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07973"],
4428                ..Default::default()
4429            }));
4430        }
4431
4432        if !matches!(
4433            src_image_layout,
4434            ImageLayout::TransferSrcOptimal | ImageLayout::General
4435        ) {
4436            return Err(Box::new(ValidationError {
4437                context: "src_image_layout".into(),
4438                problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`"
4439                    .into(),
4440                vuids: &["VUID-VkCopyImageToBufferInfo2-srcImageLayout-01397"],
4441                ..Default::default()
4442            }));
4443        }
4444
4445        for (region_index, region) in regions.iter().enumerate() {
4446            region
4447                .validate(device)
4448                .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
4449
4450            let &BufferImageCopy {
4451                buffer_offset,
4452                buffer_row_length,
4453                buffer_image_height,
4454                ref image_subresource,
4455                image_offset,
4456                image_extent,
4457                _ne: _,
4458            } = region;
4459
4460            /*
4461               Check image
4462            */
4463
4464            if image_subresource.mip_level >= src_image.mip_levels() {
4465                return Err(Box::new(ValidationError {
4466                    problem: format!(
4467                        "`regions[{}].image_subresource.mip_level` is not less than \
4468                        `src_image.mip_levels()`",
4469                        region_index
4470                    )
4471                    .into(),
4472                    vuids: &["VUID-VkCopyImageToBufferInfo2-imageSubresource-07967"],
4473                    ..Default::default()
4474                }));
4475            }
4476
4477            let mut image_subresource_format = src_image_format;
4478            let mut image_subresource_extent =
4479                mip_level_extent(src_image.extent(), image_subresource.mip_level).unwrap();
4480
4481            if src_image_format_planes.is_empty() {
4482                if !src_image_format_aspects.contains(image_subresource.aspects) {
4483                    return Err(Box::new(ValidationError {
4484                        problem: format!(
4485                            "`regions[{}].image_subresource.aspects` is not a subset of \
4486                            `src_image.format().aspects()`",
4487                            region_index
4488                        )
4489                        .into(),
4490                        vuids: &["VUID-VkCopyImageToBufferInfo2-aspectMask-00211"],
4491                        ..Default::default()
4492                    }));
4493                }
4494            } else if src_image_format_planes.len() == 2 {
4495                match image_subresource.aspects {
4496                    ImageAspects::PLANE_0 => {
4497                        image_subresource_format = src_image_format_planes[0];
4498                    }
4499                    ImageAspects::PLANE_1 => {
4500                        image_subresource_format = src_image_format_planes[1];
4501                        image_subresource_extent = src_image_format_subsampled_extent;
4502                    }
4503                    _ => {
4504                        return Err(Box::new(ValidationError {
4505                            problem: format!(
4506                                "`src_image.format()` is a multi-planar format with two planes, \
4507                                but `regions[{}].image_subresource.aspect` is not \
4508                                `ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`",
4509                                region_index,
4510                            )
4511                            .into(),
4512                            vuids: &[
4513                                "VUID-VkCopyImageToBufferInfo2-srcImage-07981",
4514                                "VUID-VkCopyImageToBufferInfo2-aspectMask-00211",
4515                            ],
4516                            ..Default::default()
4517                        }));
4518                    }
4519                }
4520            } else if src_image_format_planes.len() == 3 {
4521                match image_subresource.aspects {
4522                    ImageAspects::PLANE_0 => {
4523                        image_subresource_format = src_image_format_planes[0];
4524                    }
4525                    ImageAspects::PLANE_1 => {
4526                        image_subresource_format = src_image_format_planes[1];
4527                        image_subresource_extent = src_image_format_subsampled_extent;
4528                    }
4529                    ImageAspects::PLANE_2 => {
4530                        image_subresource_format = src_image_format_planes[2];
4531                        image_subresource_extent = src_image_format_subsampled_extent;
4532                    }
4533                    _ => {
4534                        return Err(Box::new(ValidationError {
4535                            problem: format!(
4536                                "`src_image.format()` is a multi-planar format with three planes, \
4537                                but `regions[{}].image_subresource.aspect` is not \
4538                                `ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \
4539                                `ImageAspects::PLANE_2`",
4540                                region_index,
4541                            )
4542                            .into(),
4543                            vuids: &[
4544                                "VUID-VkCopyImageToBufferInfo2-srcImage-07982",
4545                                "VUID-VkCopyImageToBufferInfo2-aspectMask-00211",
4546                            ],
4547                            ..Default::default()
4548                        }));
4549                    }
4550                }
4551            }
4552
4553            match src_image.image_type() {
4554                ImageType::Dim1d => {
4555                    if image_offset[1] != 0 {
4556                        return Err(Box::new(ValidationError {
4557                            problem: format!(
4558                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
4559                                `regions[{}].image_offset[1]` is not 0",
4560                                region_index,
4561                            )
4562                            .into(),
4563                            vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07979"],
4564                            ..Default::default()
4565                        }));
4566                    }
4567
4568                    if image_extent[1] != 1 {
4569                        return Err(Box::new(ValidationError {
4570                            problem: format!(
4571                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
4572                                `regions[{}].image_extent[1]` is not 1",
4573                                region_index,
4574                            )
4575                            .into(),
4576                            vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07979"],
4577                            ..Default::default()
4578                        }));
4579                    }
4580
4581                    if image_offset[2] != 0 {
4582                        return Err(Box::new(ValidationError {
4583                            problem: format!(
4584                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
4585                                `regions[{}].image_offset[2]` is not 0",
4586                                region_index,
4587                            )
4588                            .into(),
4589                            vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"],
4590                            ..Default::default()
4591                        }));
4592                    }
4593
4594                    if image_extent[2] != 1 {
4595                        return Err(Box::new(ValidationError {
4596                            problem: format!(
4597                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
4598                                `regions[{}].image_extent[2]` is not 1",
4599                                region_index,
4600                            )
4601                            .into(),
4602                            vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"],
4603                            ..Default::default()
4604                        }));
4605                    }
4606                }
4607                ImageType::Dim2d => {
4608                    if image_offset[2] != 0 {
4609                        return Err(Box::new(ValidationError {
4610                            problem: format!(
4611                                "`src_image.image_type()` is `ImageType::Dim2d`, but \
4612                                `regions[{}].image_offset[2]` is not 0",
4613                                region_index,
4614                            )
4615                            .into(),
4616                            vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"],
4617                            ..Default::default()
4618                        }));
4619                    }
4620
4621                    if image_extent[2] != 1 {
4622                        return Err(Box::new(ValidationError {
4623                            problem: format!(
4624                                "`src_image.image_type()` is `ImageType::Dim2d`, but \
4625                                `regions[{}].image_extent[2]` is not 1",
4626                                region_index,
4627                            )
4628                            .into(),
4629                            vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"],
4630                            ..Default::default()
4631                        }));
4632                    }
4633                }
4634                ImageType::Dim3d => {
4635                    if image_subresource.array_layers != (0..1) {
4636                        return Err(Box::new(ValidationError {
4637                            problem: format!(
4638                                "`src_image.image_type()` is  `ImageType::Dim3d`, but \
4639                                `regions[{}].image_subresource.array_layers` is not `0..1`",
4640                                region_index,
4641                            )
4642                            .into(),
4643                            vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07983"],
4644                            ..Default::default()
4645                        }));
4646                    }
4647                }
4648            }
4649
4650            if image_subresource.array_layers.end > src_image.array_layers() {
4651                return Err(Box::new(ValidationError {
4652                    problem: format!(
4653                        "`regions[{}].dst_subresource.array_layers.end` is not less than \
4654                        `src_image.array_layers()`",
4655                        region_index
4656                    )
4657                    .into(),
4658                    vuids: &["VUID-VkCopyImageToBufferInfo2-imageSubresource-07968"],
4659                    ..Default::default()
4660                }));
4661            }
4662
4663            if image_offset[0] + image_extent[0] > image_subresource_extent[0] {
4664                return Err(Box::new(ValidationError {
4665                    problem: format!(
4666                        "`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is greater \
4667                        than coordinate 0 of the extent of the subresource of `src_image` \
4668                        selected by `regions[{0}].image_subresource`",
4669                        region_index,
4670                    )
4671                    .into(),
4672                    vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00197"],
4673                    ..Default::default()
4674                }));
4675            }
4676
4677            if image_offset[1] + image_extent[1] > image_subresource_extent[1] {
4678                return Err(Box::new(ValidationError {
4679                    problem: format!(
4680                        "`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is greater \
4681                        than coordinate 1 of the extent of the subresource of `src_image` \
4682                        selected by `regions[{0}].image_subresource`",
4683                        region_index,
4684                    )
4685                    .into(),
4686                    vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00198"],
4687                    ..Default::default()
4688                }));
4689            }
4690
4691            if image_offset[2] + image_extent[2] > image_subresource_extent[2] {
4692                return Err(Box::new(ValidationError {
4693                    problem: format!(
4694                        "`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is greater \
4695                        than coordinate 2 of the extent of the subresource of `src_image` \
4696                        selected by `regions[{0}].image_subresource`",
4697                        region_index,
4698                    )
4699                    .into(),
4700                    vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00200"],
4701                    ..Default::default()
4702                }));
4703            }
4704
4705            let image_subresource_format_block_extent = image_subresource_format.block_extent();
4706
4707            if image_offset[0] % image_subresource_format_block_extent[0] != 0 {
4708                return Err(Box::new(ValidationError {
4709                    problem: format!(
4710                        "`regions[{0}].image_offset[0]` is not a multiple of coordinate 0 of the \
4711                        block extent of the format of the subresource of `src_image` \
4712                        selected by `regions[{0}].image_subresource`",
4713                        region_index,
4714                    )
4715                    .into(),
4716                    vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07274"],
4717                    ..Default::default()
4718                }));
4719            }
4720
4721            if image_offset[1] % image_subresource_format_block_extent[1] != 0 {
4722                return Err(Box::new(ValidationError {
4723                    problem: format!(
4724                        "`regions[{0}].image_offset[1]` is not a multiple of coordinate 1 of the \
4725                        block extent of the format of the subresource of `src_image` \
4726                        selected by `regions[{0}].image_subresource`",
4727                        region_index,
4728                    )
4729                    .into(),
4730                    vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07275"],
4731                    ..Default::default()
4732                }));
4733            }
4734
4735            if image_offset[2] % image_subresource_format_block_extent[2] != 0 {
4736                return Err(Box::new(ValidationError {
4737                    problem: format!(
4738                        "`regions[{0}].image_offset[2]` is not a multiple of coordinate 2 of the \
4739                        block extent of the format of the subresource of `src_image` \
4740                        selected by `regions[{0}].image_subresource`",
4741                        region_index,
4742                    )
4743                    .into(),
4744                    vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07276"],
4745                    ..Default::default()
4746                }));
4747            }
4748
4749            if image_offset[0] + image_extent[0] != image_subresource_extent[0]
4750                && (image_offset[0] + image_extent[0]) % image_subresource_format_block_extent[0]
4751                    != 0
4752            {
4753                return Err(Box::new(ValidationError {
4754                    problem: format!(
4755                        "`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is not \
4756                        equal to the extent of the subresource of `src_image` \
4757                        selected by `regions[{0}].image_subresource`, but \
4758                        it is also not a multiple of coordinate 0 of the block extent of the \
4759                        format of that subresource",
4760                        region_index,
4761                    )
4762                    .into(),
4763                    vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00207"],
4764                    ..Default::default()
4765                }));
4766            }
4767
4768            if image_offset[1] + image_extent[1] != image_subresource_extent[1]
4769                && (image_offset[1] + image_extent[1]) % image_subresource_format_block_extent[1]
4770                    != 0
4771            {
4772                return Err(Box::new(ValidationError {
4773                    problem: format!(
4774                        "`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is not \
4775                        equal to the extent of the subresource of `src_image` \
4776                        selected by `regions[{0}].image_subresource`, but \
4777                        it is also not a multiple of coordinate 1 of the block extent of the \
4778                        format of that subresource",
4779                        region_index,
4780                    )
4781                    .into(),
4782                    vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00208"],
4783                    ..Default::default()
4784                }));
4785            }
4786
4787            if image_offset[2] + image_extent[2] != image_subresource_extent[2]
4788                && (image_offset[2] + image_extent[2]) % image_subresource_format_block_extent[2]
4789                    != 0
4790            {
4791                return Err(Box::new(ValidationError {
4792                    problem: format!(
4793                        "`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is not \
4794                        equal to the extent of the subresource of `src_image` \
4795                        selected by `regions[{0}].image_subresource`, but \
4796                        it is also not a multiple of coordinate 2 of the block extent of the \
4797                        format of that subresource",
4798                        region_index,
4799                    )
4800                    .into(),
4801                    vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00209"],
4802                    ..Default::default()
4803                }));
4804            }
4805
4806            /*
4807               Check buffer and image together
4808            */
4809
4810            let image_subresource_format_block_size = image_subresource_format.block_size();
4811
4812            if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
4813                if (dst_buffer.offset() + buffer_offset) % 4 != 0 {
4814                    return Err(Box::new(ValidationError {
4815                        problem: format!(
4816                            "`src_image.format()` is a depth/stencil format, but \
4817                            `dst_buffer.offset() + regions[{0}].buffer_offset` is not a \
4818                            multiple of 4",
4819                            region_index,
4820                        )
4821                        .into(),
4822                        vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07978"],
4823                        ..Default::default()
4824                    }));
4825                }
4826            } else {
4827                if (dst_buffer.offset() + buffer_offset) % image_subresource_format_block_size != 0
4828                {
4829                    return Err(Box::new(ValidationError {
4830                        problem: format!(
4831                            "`src_image.format()` is not a depth/stencil format, but \
4832                            `dst_buffer.offset() + regions[{0}].buffer_offset` is not a \
4833                            multiple of the block size of the format of the subresource of \
4834                            `src_image` selected by `regions[{0}].image_subresource`",
4835                            region_index,
4836                        )
4837                        .into(),
4838                        vuids: &[
4839                            "VUID-VkCopyImageToBufferInfo2-srcImage-07975",
4840                            "VUID-VkCopyImageToBufferInfo2-srcImage-07976",
4841                        ],
4842                        ..Default::default()
4843                    }));
4844                }
4845            }
4846
4847            if buffer_row_length % image_subresource_format_block_extent[0] != 0 {
4848                return Err(Box::new(ValidationError {
4849                    problem: format!(
4850                        "`regions[{0}].buffer_row_length` is not a multiple of coordinate 0 of \
4851                        the block extent of the format of the subresource of `src_image` \
4852                        selected by `regions[{0}].image_subresource`",
4853                        region_index,
4854                    )
4855                    .into(),
4856                    vuids: &["VUID-VkCopyImageToBufferInfo2-bufferRowLength-00203"],
4857                    ..Default::default()
4858                }));
4859            }
4860
4861            if buffer_image_height % image_subresource_format_block_extent[1] != 0 {
4862                return Err(Box::new(ValidationError {
4863                    problem: format!(
4864                        "`regions[{0}].buffer_image_height` is not a multiple of coordinate 1 of \
4865                        the block extent of the format of the subresource of `src_image` \
4866                        selected by `regions[{0}].image_subresource`",
4867                        region_index,
4868                    )
4869                    .into(),
4870                    vuids: &["VUID-VkCopyImageToBufferInfo2-bufferImageHeight-00204"],
4871                    ..Default::default()
4872                }));
4873            }
4874
4875            if (buffer_row_length / image_subresource_format_block_extent[0]) as DeviceSize
4876                * image_subresource_format_block_size
4877                > 0x7FFFFFFF
4878            {
4879                return Err(Box::new(ValidationError {
4880                    problem: format!(
4881                        "`regions[{0}].buffer_row_length`, divided by the block size of the \
4882                        format of the subresource of `src_image` selected by \
4883                        `regions[{0}].image_subresource`, and then multiplied by the block size \
4884                        of that subresource, is greater than 0x7FFFFFFF",
4885                        region_index,
4886                    )
4887                    .into(),
4888                    vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07277"],
4889                    ..Default::default()
4890                }));
4891            }
4892
4893            if buffer_offset + region.buffer_copy_size(image_subresource_format) > dst_buffer.size()
4894            {
4895                return Err(Box::new(ValidationError {
4896                    problem: format!(
4897                        "`regions[{0}].buffer_offset` plus the number of bytes being copied \
4898                        is greater than `dst_buffer.size()`",
4899                        region_index,
4900                    )
4901                    .into(),
4902                    vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-00183"],
4903                    ..Default::default()
4904                }));
4905            }
4906        }
4907
4908        // VUID-VkCopyImageToBufferInfo2-pRegions-00184
4909        // Can't occur as long as memory aliasing isn't allowed.
4910
4911        Ok(())
4912    }
4913
4914    pub(crate) fn to_vk2<'a>(
4915        &self,
4916        regions_vk: &'a [ash::vk::BufferImageCopy2<'static>],
4917    ) -> ash::vk::CopyImageToBufferInfo2<'a> {
4918        let &Self {
4919            ref src_image,
4920            src_image_layout,
4921            ref dst_buffer,
4922            regions: _,
4923            _ne: _,
4924        } = self;
4925
4926        ash::vk::CopyImageToBufferInfo2::default()
4927            .src_image(src_image.handle())
4928            .src_image_layout(src_image_layout.into())
4929            .dst_buffer(dst_buffer.buffer().handle())
4930            .regions(regions_vk)
4931    }
4932
4933    pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::BufferImageCopy2<'static>; 8]> {
4934        let Self {
4935            dst_buffer,
4936            regions,
4937            ..
4938        } = self;
4939
4940        regions
4941            .iter()
4942            .map(|region| {
4943                let mut region_vk = region.to_vk2();
4944                region_vk.buffer_offset += dst_buffer.offset();
4945                region_vk
4946            })
4947            .collect()
4948    }
4949
4950    pub(crate) fn to_vk(&self) -> CopyImageToBufferInfoVk {
4951        let &Self {
4952            ref src_image,
4953            src_image_layout,
4954            ref dst_buffer,
4955            regions: _,
4956            _ne: _,
4957        } = self;
4958
4959        CopyImageToBufferInfoVk {
4960            src_image_vk: src_image.handle(),
4961            src_image_layout_vk: src_image_layout.into(),
4962            dst_buffer_vk: dst_buffer.buffer().handle(),
4963        }
4964    }
4965
4966    pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::BufferImageCopy; 8]> {
4967        let Self {
4968            dst_buffer,
4969            regions,
4970            ..
4971        } = self;
4972
4973        regions
4974            .iter()
4975            .map(|region| {
4976                let mut region_vk = region.to_vk();
4977                region_vk.buffer_offset += dst_buffer.offset();
4978                region_vk
4979            })
4980            .collect()
4981    }
4982}
4983
4984pub(crate) struct CopyImageToBufferInfoVk {
4985    pub(crate) src_image_vk: ash::vk::Image,
4986    pub(crate) src_image_layout_vk: ash::vk::ImageLayout,
4987    pub(crate) dst_buffer_vk: ash::vk::Buffer,
4988}
4989
4990/// A region of data to copy between a buffer and an image.
4991#[derive(Clone, Debug)]
4992pub struct BufferImageCopy {
4993    /// The offset in bytes from the start of the buffer that copying will start from.
4994    ///
4995    /// The default value is `0`.
4996    pub buffer_offset: DeviceSize,
4997
4998    /// The number of texels between successive rows of image data in the buffer.
4999    ///
5000    /// If set to `0`, the width of the image is used.
5001    ///
5002    /// The default value is `0`.
5003    pub buffer_row_length: u32,
5004
5005    /// The number of rows between successive depth slices of image data in the buffer.
5006    ///
5007    /// If set to `0`, the height of the image is used.
5008    ///
5009    /// The default value is `0`.
5010    pub buffer_image_height: u32,
5011
5012    /// The subresource of the image to copy from/to.
5013    ///
5014    /// The default value is empty, which must be overridden.
5015    pub image_subresource: ImageSubresourceLayers,
5016
5017    /// The offset from the zero coordinate of the image that copying will start from.
5018    ///
5019    /// The default value is `[0; 3]`.
5020    pub image_offset: [u32; 3],
5021
5022    /// The extent of texels in the image to copy.
5023    ///
5024    /// The default value is `[0; 3]`, which must be overridden.
5025    pub image_extent: [u32; 3],
5026
5027    pub _ne: crate::NonExhaustive,
5028}
5029
5030impl Default for BufferImageCopy {
5031    #[inline]
5032    fn default() -> Self {
5033        Self {
5034            buffer_offset: 0,
5035            buffer_row_length: 0,
5036            buffer_image_height: 0,
5037            image_subresource: ImageSubresourceLayers {
5038                aspects: ImageAspects::empty(),
5039                mip_level: 0,
5040                array_layers: 0..0,
5041            },
5042            image_offset: [0; 3],
5043            image_extent: [0; 3],
5044            _ne: crate::NonExhaustive(()),
5045        }
5046    }
5047}
5048
5049impl BufferImageCopy {
5050    // Following
5051    // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap20.html#copies-buffers-images-addressing
5052    pub(crate) fn buffer_copy_size(&self, format: Format) -> DeviceSize {
5053        let &BufferImageCopy {
5054            buffer_offset: _,
5055            mut buffer_row_length,
5056            mut buffer_image_height,
5057            ref image_subresource,
5058            image_offset: _,
5059            mut image_extent,
5060            _ne: _,
5061        } = self;
5062
5063        if buffer_row_length == 0 {
5064            buffer_row_length = image_extent[0];
5065        }
5066
5067        if buffer_image_height == 0 {
5068            buffer_image_height = image_extent[1];
5069        }
5070
5071        // Scale down from texels to texel blocks, rounding up if needed.
5072        let block_extent = format.block_extent();
5073        buffer_row_length = buffer_row_length.div_ceil(block_extent[0]);
5074        buffer_image_height = buffer_image_height.div_ceil(block_extent[1]);
5075
5076        for i in 0..3 {
5077            image_extent[i] = image_extent[i].div_ceil(block_extent[i]);
5078        }
5079
5080        // Only one of these is greater than 1, take the greater number.
5081        image_extent[2] = max(
5082            image_extent[2],
5083            image_subresource.array_layers.end - image_subresource.array_layers.start,
5084        );
5085
5086        let blocks_to_last_slice = (image_extent[2] as DeviceSize - 1)
5087            * buffer_image_height as DeviceSize
5088            * buffer_row_length as DeviceSize;
5089        let blocks_to_last_row =
5090            (image_extent[1] as DeviceSize - 1) * buffer_row_length as DeviceSize;
5091        let num_blocks = blocks_to_last_slice + blocks_to_last_row + image_extent[0] as DeviceSize;
5092
5093        num_blocks * format.block_size()
5094    }
5095
5096    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
5097        let &Self {
5098            buffer_offset: _,
5099            buffer_row_length,
5100            buffer_image_height,
5101            ref image_subresource,
5102            image_offset: _,
5103            image_extent,
5104            _ne: _,
5105        } = self;
5106
5107        image_subresource
5108            .validate(device)
5109            .map_err(|err| err.add_context("image_subresource"))?;
5110
5111        if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) {
5112            return Err(Box::new(ValidationError {
5113                problem: "`buffer_row_length` is not either zero, or greater than or equal to \
5114                    `image_extent[0]`"
5115                    .into(),
5116                vuids: &["VUID-VkBufferImageCopy2-bufferRowLength-00195"],
5117                ..Default::default()
5118            }));
5119        }
5120
5121        if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) {
5122            return Err(Box::new(ValidationError {
5123                problem: "`buffer_image_height` is not either zero, or greater than or equal to \
5124                    `image_extent[1]`"
5125                    .into(),
5126                vuids: &["VUID-VkBufferImageCopy2-bufferImageHeight-00196"],
5127                ..Default::default()
5128            }));
5129        }
5130
5131        if image_subresource.aspects.count() != 1 {
5132            return Err(Box::new(ValidationError {
5133                context: "image_subresource.aspects".into(),
5134                problem: "contains more than one aspect".into(),
5135                vuids: &["VUID-VkBufferImageCopy2-aspectMask-00212"],
5136                ..Default::default()
5137            }));
5138        }
5139
5140        if image_extent[0] == 0 {
5141            return Err(Box::new(ValidationError {
5142                context: "image_extent[0]".into(),
5143                problem: "is zero".into(),
5144                vuids: &["VUID-VkBufferImageCopy2-imageExtent-06659"],
5145                ..Default::default()
5146            }));
5147        }
5148
5149        if image_extent[1] == 0 {
5150            return Err(Box::new(ValidationError {
5151                context: "image_extent[1]".into(),
5152                problem: "is zero".into(),
5153                vuids: &["VUID-VkBufferImageCopy2-imageExtent-06660"],
5154                ..Default::default()
5155            }));
5156        }
5157
5158        if image_extent[2] == 0 {
5159            return Err(Box::new(ValidationError {
5160                context: "image_extent[2]".into(),
5161                problem: "is zero".into(),
5162                vuids: &["VUID-VkBufferImageCopy2-imageExtent-06661"],
5163                ..Default::default()
5164            }));
5165        }
5166
5167        Ok(())
5168    }
5169
5170    pub(crate) fn to_vk2(&self) -> ash::vk::BufferImageCopy2<'static> {
5171        let &Self {
5172            buffer_offset,
5173            buffer_row_length,
5174            buffer_image_height,
5175            ref image_subresource,
5176            image_offset,
5177            image_extent,
5178            _ne: _,
5179        } = self;
5180
5181        ash::vk::BufferImageCopy2::default()
5182            .buffer_offset(buffer_offset)
5183            .buffer_row_length(buffer_row_length)
5184            .buffer_image_height(buffer_image_height)
5185            .image_subresource(image_subresource.to_vk())
5186            .image_offset(ash::vk::Offset3D {
5187                x: image_offset[0] as i32,
5188                y: image_offset[1] as i32,
5189                z: image_offset[2] as i32,
5190            })
5191            .image_extent(ash::vk::Extent3D {
5192                width: image_extent[0],
5193                height: image_extent[1],
5194                depth: image_extent[2],
5195            })
5196    }
5197
5198    pub(crate) fn to_vk(&self) -> ash::vk::BufferImageCopy {
5199        let &Self {
5200            buffer_offset,
5201            buffer_row_length,
5202            buffer_image_height,
5203            ref image_subresource,
5204            image_offset,
5205            image_extent,
5206            _ne: _,
5207        } = self;
5208
5209        ash::vk::BufferImageCopy {
5210            buffer_offset,
5211            buffer_row_length,
5212            buffer_image_height,
5213            image_subresource: image_subresource.to_vk(),
5214            image_offset: ash::vk::Offset3D {
5215                x: image_offset[0] as i32,
5216                y: image_offset[1] as i32,
5217                z: image_offset[2] as i32,
5218            },
5219            image_extent: ash::vk::Extent3D {
5220                width: image_extent[0],
5221                height: image_extent[1],
5222                depth: image_extent[2],
5223            },
5224        }
5225    }
5226}
5227
5228/// Parameters to blit image data.
5229#[derive(Clone, Debug)]
5230pub struct BlitImageInfo {
5231    /// The image to blit from.
5232    ///
5233    /// There is no default value.
5234    pub src_image: Arc<Image>,
5235
5236    /// The layout used for `src_image` during the blit operation.
5237    ///
5238    /// The following layouts are allowed:
5239    /// - [`ImageLayout::TransferSrcOptimal`]
5240    /// - [`ImageLayout::General`]
5241    ///
5242    /// The default value is [`ImageLayout::TransferSrcOptimal`].
5243    pub src_image_layout: ImageLayout,
5244
5245    /// The image to blit to.
5246    ///
5247    /// There is no default value.
5248    pub dst_image: Arc<Image>,
5249
5250    /// The layout used for `dst_image` during the blit operation.
5251    ///
5252    /// The following layouts are allowed:
5253    /// - [`ImageLayout::TransferDstOptimal`]
5254    /// - [`ImageLayout::General`]
5255    ///
5256    /// The default value is [`ImageLayout::TransferDstOptimal`].
5257    pub dst_image_layout: ImageLayout,
5258
5259    /// The regions of both images to blit between.
5260    ///
5261    /// The default value is a single region, covering the first mip level, and the smallest of the
5262    /// array layers of the two images. The whole extent of each image is covered, scaling if
5263    /// necessary. All aspects of each image are selected, or `plane0` if the image is
5264    /// multi-planar.
5265    pub regions: SmallVec<[ImageBlit; 1]>,
5266
5267    /// The filter to use for sampling `src_image` when the `src_extent` and
5268    /// `dst_extent` of a region are not the same size.
5269    ///
5270    /// The default value is [`Filter::Nearest`].
5271    pub filter: Filter,
5272
5273    pub _ne: crate::NonExhaustive,
5274}
5275
5276impl BlitImageInfo {
5277    /// Returns a `BlitImageInfo` with the specified `src_image` and `dst_image`.
5278    #[inline]
5279    pub fn images(src_image: Arc<Image>, dst_image: Arc<Image>) -> Self {
5280        let min_array_layers = src_image.array_layers().min(dst_image.array_layers());
5281        let region = ImageBlit {
5282            src_subresource: ImageSubresourceLayers {
5283                array_layers: 0..min_array_layers,
5284                ..src_image.subresource_layers()
5285            },
5286            src_offsets: [[0; 3], src_image.extent()],
5287            dst_subresource: ImageSubresourceLayers {
5288                array_layers: 0..min_array_layers,
5289                ..dst_image.subresource_layers()
5290            },
5291            dst_offsets: [[0; 3], dst_image.extent()],
5292            ..Default::default()
5293        };
5294
5295        Self {
5296            src_image,
5297            src_image_layout: ImageLayout::TransferSrcOptimal,
5298            dst_image,
5299            dst_image_layout: ImageLayout::TransferDstOptimal,
5300            regions: smallvec![region],
5301            filter: Filter::Nearest,
5302            _ne: crate::NonExhaustive(()),
5303        }
5304    }
5305
5306    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
5307        let &Self {
5308            ref src_image,
5309            src_image_layout,
5310            ref dst_image,
5311            dst_image_layout,
5312            ref regions,
5313            filter,
5314            _ne: _,
5315        } = self;
5316
5317        src_image_layout.validate_device(device).map_err(|err| {
5318            err.add_context("src_image_layout")
5319                .set_vuids(&["VUID-VkBlitImageInfo2-srcImageLayout-parameter"])
5320        })?;
5321
5322        dst_image_layout.validate_device(device).map_err(|err| {
5323            err.add_context("dst_image_layout")
5324                .set_vuids(&["VUID-VkBlitImageInfo2-dstImageLayout-parameter"])
5325        })?;
5326
5327        filter.validate_device(device).map_err(|err| {
5328            err.add_context("filter")
5329                .set_vuids(&["VUID-VkBlitImageInfo2-filter-parameter"])
5330        })?;
5331
5332        // VUID-VkBlitImageInfo2-commonparent
5333        assert_eq!(device, src_image.device().as_ref());
5334        assert_eq!(device, dst_image.device().as_ref());
5335
5336        let src_image_format = src_image.format();
5337        let src_image_format_aspects = src_image_format.aspects();
5338
5339        let dst_image_format = dst_image.format();
5340        let dst_image_format_aspects = dst_image_format.aspects();
5341
5342        if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) {
5343            return Err(Box::new(ValidationError {
5344                context: "src_image.usage()".into(),
5345                problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(),
5346                vuids: &["VUID-VkBlitImageInfo2-srcImage-00219"],
5347                ..Default::default()
5348            }));
5349        }
5350
5351        if !src_image
5352            .format_features()
5353            .intersects(FormatFeatures::BLIT_SRC)
5354        {
5355            return Err(Box::new(ValidationError {
5356                context: "src_image.format_features()".into(),
5357                problem: "does not contain `FormatFeatures::BLIT_SRC`".into(),
5358                vuids: &["VUID-VkBlitImageInfo2-srcImage-01999"],
5359                ..Default::default()
5360            }));
5361        }
5362
5363        if src_image_format.ycbcr_chroma_sampling().is_some() {
5364            return Err(Box::new(ValidationError {
5365                context: "src_image.format()".into(),
5366                problem: "is a YCbCr format".into(),
5367                vuids: &["VUID-VkBlitImageInfo2-srcImage-06421"],
5368                ..Default::default()
5369            }));
5370        }
5371
5372        if src_image.samples() != SampleCount::Sample1 {
5373            return Err(Box::new(ValidationError {
5374                context: "src_image.samples()".into(),
5375                problem: "is not `SampleCount::Sample1`".into(),
5376                vuids: &["VUID-VkBlitImageInfo2-srcImage-00233"],
5377                ..Default::default()
5378            }));
5379        }
5380
5381        if !matches!(
5382            src_image_layout,
5383            ImageLayout::TransferSrcOptimal | ImageLayout::General
5384        ) {
5385            return Err(Box::new(ValidationError {
5386                context: "src_image_layout".into(),
5387                problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`"
5388                    .into(),
5389                vuids: &["VUID-VkBlitImageInfo2-srcImageLayout-01398"],
5390                ..Default::default()
5391            }));
5392        }
5393
5394        if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) {
5395            return Err(Box::new(ValidationError {
5396                context: "dst_image.usage()".into(),
5397                problem: "does not contain `ImageUsage::TRANSFER_DST`".into(),
5398                vuids: &["VUID-VkBlitImageInfo2-dstImage-00224"],
5399                ..Default::default()
5400            }));
5401        }
5402
5403        if !dst_image
5404            .format_features()
5405            .intersects(FormatFeatures::BLIT_DST)
5406        {
5407            return Err(Box::new(ValidationError {
5408                context: "dst_image.format_features()".into(),
5409                problem: "does not contain `FormatFeatures::BLIT_DST`".into(),
5410                vuids: &["VUID-VkBlitImageInfo2-dstImage-02000"],
5411                ..Default::default()
5412            }));
5413        }
5414
5415        if dst_image_format.ycbcr_chroma_sampling().is_some() {
5416            return Err(Box::new(ValidationError {
5417                context: "dst_image.format()".into(),
5418                problem: "is a YCbCr format".into(),
5419                vuids: &["VUID-VkBlitImageInfo2-dstImage-06422"],
5420                ..Default::default()
5421            }));
5422        }
5423
5424        if dst_image.samples() != SampleCount::Sample1 {
5425            return Err(Box::new(ValidationError {
5426                context: "dst_image.samples()".into(),
5427                problem: "is not `SampleCount::Sample1`".into(),
5428                vuids: &["VUID-VkBlitImageInfo2-dstImage-00234"],
5429                ..Default::default()
5430            }));
5431        }
5432
5433        if !matches!(
5434            dst_image_layout,
5435            ImageLayout::TransferDstOptimal | ImageLayout::General
5436        ) {
5437            return Err(Box::new(ValidationError {
5438                context: "dst_image_layout".into(),
5439                problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
5440                    .into(),
5441                vuids: &["VUID-VkBlitImageInfo2-dstImageLayout-01399"],
5442                ..Default::default()
5443            }));
5444        }
5445
5446        if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
5447            || dst_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
5448        {
5449            if src_image_format != dst_image_format {
5450                return Err(Box::new(ValidationError {
5451                    problem: "one of `src_image.format()` or `dst_image.format()` is a \
5452                        depth/stencil format, but they are not equal"
5453                        .into(),
5454                    vuids: &["VUID-VkBlitImageInfo2-srcImage-00231"],
5455                    ..Default::default()
5456                }));
5457            }
5458        } else {
5459            if src_image_format
5460                .numeric_format_color()
5461                .unwrap()
5462                .numeric_type()
5463                != dst_image_format
5464                    .numeric_format_color()
5465                    .unwrap()
5466                    .numeric_type()
5467            {
5468                return Err(Box::new(ValidationError {
5469                    problem: "neither `src_image.format()` nor `dst_image.format()` is a \
5470                        depth/stencil format, but their numeric types are not equal"
5471                        .into(),
5472                    vuids: &[
5473                        "VUID-VkBlitImageInfo2-srcImage-00229",
5474                        "VUID-VkBlitImageInfo2-srcImage-00230",
5475                    ],
5476                    ..Default::default()
5477                }));
5478            }
5479        }
5480
5481        if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
5482            && filter != Filter::Nearest
5483        {
5484            return Err(Box::new(ValidationError {
5485                problem: "`src_image.format()` is a depth/stencil format, but \
5486                    `filter` is not `Filter::Nearest`"
5487                    .into(),
5488                vuids: &["VUID-VkBlitImageInfo2-srcImage-00232"],
5489                ..Default::default()
5490            }));
5491        }
5492
5493        match filter {
5494            Filter::Nearest => (),
5495            Filter::Linear => {
5496                if !src_image
5497                    .format_features()
5498                    .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR)
5499                {
5500                    return Err(Box::new(ValidationError {
5501                        problem: "`filter` is `Filter::Linear`, but \
5502                            `src_image.format_features()` do not contain \
5503                            `FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR`"
5504                            .into(),
5505                        vuids: &["VUID-VkBlitImageInfo2-filter-02001"],
5506                        ..Default::default()
5507                    }));
5508                }
5509            }
5510            Filter::Cubic => {
5511                if !src_image
5512                    .format_features()
5513                    .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC)
5514                {
5515                    return Err(Box::new(ValidationError {
5516                        problem: "`filter` is `Filter::Cubic`, but \
5517                            `src_image.format_features()` do not contain \
5518                            `FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC`"
5519                            .into(),
5520                        vuids: &["VUID-VkBlitImageInfo2-filter-02002"],
5521                        ..Default::default()
5522                    }));
5523                }
5524
5525                if src_image.image_type() != ImageType::Dim2d {
5526                    return Err(Box::new(ValidationError {
5527                        problem: "`filter` is `Filter::Cubic`, but \
5528                            `src_image.image_type()` is not `ImageType::Dim2d`"
5529                            .into(),
5530                        vuids: &["VUID-VkBlitImageInfo2-filter-00237"],
5531                        ..Default::default()
5532                    }));
5533                }
5534            }
5535        }
5536
5537        let is_same_image = src_image == dst_image;
5538        let mut overlap_subresource_indices = None;
5539        let mut overlap_extent_indices = None;
5540
5541        for (region_index, region) in regions.iter().enumerate() {
5542            region
5543                .validate(device)
5544                .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
5545
5546            let &ImageBlit {
5547                ref src_subresource,
5548                src_offsets,
5549                ref dst_subresource,
5550                dst_offsets,
5551                _ne: _,
5552            } = region;
5553
5554            /*
5555               Check src
5556            */
5557
5558            if src_subresource.mip_level >= src_image.mip_levels() {
5559                return Err(Box::new(ValidationError {
5560                    problem: format!(
5561                        "`regions[{}].src_subresource.mip_level` is not less than \
5562                        `src_image.mip_levels()`",
5563                        region_index
5564                    )
5565                    .into(),
5566                    vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01705"],
5567                    ..Default::default()
5568                }));
5569            }
5570
5571            let src_subresource_extent =
5572                mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap();
5573
5574            if !src_image_format_aspects.contains(src_subresource.aspects) {
5575                return Err(Box::new(ValidationError {
5576                    problem: format!(
5577                        "`regions[{}].src_subresource.aspects` is not a subset of \
5578                        `src_image.format().aspects()`",
5579                        region_index
5580                    )
5581                    .into(),
5582                    vuids: &["VUID-VkBlitImageInfo2-aspectMask-00241"],
5583                    ..Default::default()
5584                }));
5585            }
5586
5587            match src_image.image_type() {
5588                ImageType::Dim1d => {
5589                    if src_offsets[0][1] != 0 {
5590                        return Err(Box::new(ValidationError {
5591                            problem: format!(
5592                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
5593                                `regions[{}].src_offsets[0][1]` is not 0",
5594                                region_index,
5595                            )
5596                            .into(),
5597                            vuids: &["VUID-VkBlitImageInfo2-srcImage-00245"],
5598                            ..Default::default()
5599                        }));
5600                    }
5601
5602                    if src_offsets[1][1] != 1 {
5603                        return Err(Box::new(ValidationError {
5604                            problem: format!(
5605                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
5606                                `regions[{}].src_offsets[1][1]` is not 1",
5607                                region_index,
5608                            )
5609                            .into(),
5610                            vuids: &["VUID-VkBlitImageInfo2-srcImage-00245"],
5611                            ..Default::default()
5612                        }));
5613                    }
5614
5615                    if src_offsets[0][2] != 0 {
5616                        return Err(Box::new(ValidationError {
5617                            problem: format!(
5618                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
5619                                `regions[{}].src_offsets[0][2]` is not 0",
5620                                region_index,
5621                            )
5622                            .into(),
5623                            vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"],
5624                            ..Default::default()
5625                        }));
5626                    }
5627
5628                    if src_offsets[1][2] != 1 {
5629                        return Err(Box::new(ValidationError {
5630                            problem: format!(
5631                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
5632                                `regions[{}].src_offsets[1][2]` is not 1",
5633                                region_index,
5634                            )
5635                            .into(),
5636                            vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"],
5637                            ..Default::default()
5638                        }));
5639                    }
5640                }
5641                ImageType::Dim2d => {
5642                    if src_offsets[0][2] != 0 {
5643                        return Err(Box::new(ValidationError {
5644                            problem: format!(
5645                                "`src_image.image_type()` is `ImageType::Dim2d`, but \
5646                                `regions[{}].src_offsets[0][2]` is not 0",
5647                                region_index,
5648                            )
5649                            .into(),
5650                            vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"],
5651                            ..Default::default()
5652                        }));
5653                    }
5654
5655                    if src_offsets[1][2] != 1 {
5656                        return Err(Box::new(ValidationError {
5657                            problem: format!(
5658                                "`src_image.image_type()` is `ImageType::Dim2d`, but \
5659                                `regions[{}].src_offsets[1][2]` is not 1",
5660                                region_index,
5661                            )
5662                            .into(),
5663                            vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"],
5664                            ..Default::default()
5665                        }));
5666                    }
5667                }
5668                ImageType::Dim3d => {
5669                    if src_subresource.array_layers != (0..1) {
5670                        return Err(Box::new(ValidationError {
5671                            problem: format!(
5672                                "`src_image.image_type()` is `ImageType::Dim3d`, but \
5673                                `regions[{}].src_subresource.array_layers` is not `0..1`",
5674                                region_index,
5675                            )
5676                            .into(),
5677                            vuids: &["VUID-VkBlitImageInfo2-srcImage-00240"],
5678                            ..Default::default()
5679                        }));
5680                    }
5681                }
5682            }
5683
5684            if src_subresource.array_layers.end > src_image.array_layers() {
5685                return Err(Box::new(ValidationError {
5686                    problem: format!(
5687                        "`regions[{}].src_subresource.array_layers.end` is not less than \
5688                        `src_image.array_layers()`",
5689                        region_index
5690                    )
5691                    .into(),
5692                    vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01707"],
5693                    ..Default::default()
5694                }));
5695            }
5696
5697            let src_offsets_max = [
5698                max(src_offsets[0][0], src_offsets[1][0]),
5699                max(src_offsets[0][1], src_offsets[1][1]),
5700                max(src_offsets[0][2], src_offsets[1][2]),
5701            ];
5702
5703            if src_offsets_max[0] > src_subresource_extent[0] {
5704                return Err(Box::new(ValidationError {
5705                    problem: format!(
5706                        "`max(regions[{0}].src_offsets[0][0], regions[{0}].src_offsets[1][0])` is \
5707                        greater than coordinate 0 of the extent of the subresource of \
5708                        `src_image` selected by `regions[{0}].src_subresource`",
5709                        region_index,
5710                    )
5711                    .into(),
5712                    vuids: &["VUID-VkBlitImageInfo2-srcOffset-00243"],
5713                    ..Default::default()
5714                }));
5715            }
5716
5717            if src_offsets_max[1] > src_subresource_extent[1] {
5718                return Err(Box::new(ValidationError {
5719                    problem: format!(
5720                        "`max(regions[{0}].src_offsets[0][1], regions[{0}].src_offsets[1][1])` is \
5721                        greater than coordinate 1 of the extent of the subresource of \
5722                        `src_image` selected by `regions[{0}].src_subresource`",
5723                        region_index,
5724                    )
5725                    .into(),
5726                    vuids: &["VUID-VkBlitImageInfo2-srcOffset-00244"],
5727                    ..Default::default()
5728                }));
5729            }
5730
5731            if src_offsets_max[2] > src_subresource_extent[2] {
5732                return Err(Box::new(ValidationError {
5733                    problem: format!(
5734                        "`max(regions[{0}].src_offsets[0][2], regions[{0}].src_offsets[1][2])` is \
5735                        greater than coordinate 2 of the extent of the subresource of \
5736                        `src_image` selected by `regions[{0}].src_subresource`",
5737                        region_index,
5738                    )
5739                    .into(),
5740                    vuids: &["VUID-VkBlitImageInfo2-srcOffset-00246"],
5741                    ..Default::default()
5742                }));
5743            }
5744
5745            /*
5746               Check dst
5747            */
5748
5749            if dst_subresource.mip_level >= dst_image.mip_levels() {
5750                return Err(Box::new(ValidationError {
5751                    problem: format!(
5752                        "`regions[{}].dst_subresource.mip_level` is not less than \
5753                        `dst_image.mip_levels()`",
5754                        region_index
5755                    )
5756                    .into(),
5757                    vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01705"],
5758                    ..Default::default()
5759                }));
5760            }
5761
5762            let dst_subresource_extent =
5763                mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap();
5764
5765            if !dst_image_format_aspects.contains(dst_subresource.aspects) {
5766                return Err(Box::new(ValidationError {
5767                    problem: format!(
5768                        "`regions[{}].dst_subresource.aspects` is not a subset of \
5769                        `dst_image.format().aspects()`",
5770                        region_index
5771                    )
5772                    .into(),
5773                    vuids: &["VUID-VkBlitImageInfo2-aspectMask-00242"],
5774                    ..Default::default()
5775                }));
5776            }
5777
5778            match dst_image.image_type() {
5779                ImageType::Dim1d => {
5780                    if dst_offsets[0][1] != 0 {
5781                        return Err(Box::new(ValidationError {
5782                            problem: format!(
5783                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
5784                                `regions[{}].dst_offsets[0][1]` is not 0",
5785                                region_index,
5786                            )
5787                            .into(),
5788                            vuids: &["VUID-VkBlitImageInfo2-dstImage-00250"],
5789                            ..Default::default()
5790                        }));
5791                    }
5792
5793                    if dst_offsets[1][1] != 1 {
5794                        return Err(Box::new(ValidationError {
5795                            problem: format!(
5796                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
5797                                `regions[{}].dst_offsets[1][1]` is not 1",
5798                                region_index,
5799                            )
5800                            .into(),
5801                            vuids: &["VUID-VkBlitImageInfo2-dstImage-00250"],
5802                            ..Default::default()
5803                        }));
5804                    }
5805
5806                    if dst_offsets[0][2] != 0 {
5807                        return Err(Box::new(ValidationError {
5808                            problem: format!(
5809                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
5810                                `regions[{}].dst_offsets[0][2]` is not 0",
5811                                region_index,
5812                            )
5813                            .into(),
5814                            vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"],
5815                            ..Default::default()
5816                        }));
5817                    }
5818
5819                    if dst_offsets[1][2] != 1 {
5820                        return Err(Box::new(ValidationError {
5821                            problem: format!(
5822                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
5823                                `regions[{}].dst_offsets[1][2]` is not 1",
5824                                region_index,
5825                            )
5826                            .into(),
5827                            vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"],
5828                            ..Default::default()
5829                        }));
5830                    }
5831                }
5832                ImageType::Dim2d => {
5833                    if dst_offsets[0][2] != 0 {
5834                        return Err(Box::new(ValidationError {
5835                            problem: format!(
5836                                "`dst_image.image_type()` is `ImageType::Dim2d`, but \
5837                                `regions[{}].dst_offsets[0][2]` is not 0",
5838                                region_index,
5839                            )
5840                            .into(),
5841                            vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"],
5842                            ..Default::default()
5843                        }));
5844                    }
5845
5846                    if dst_offsets[1][2] != 1 {
5847                        return Err(Box::new(ValidationError {
5848                            problem: format!(
5849                                "`dst_image.image_type()` is `ImageType::Dim2d`, but \
5850                                `regions[{}].dst_offsets[1][2]` is not 1",
5851                                region_index,
5852                            )
5853                            .into(),
5854                            vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"],
5855                            ..Default::default()
5856                        }));
5857                    }
5858                }
5859                ImageType::Dim3d => {
5860                    if dst_subresource.array_layers != (0..1) {
5861                        return Err(Box::new(ValidationError {
5862                            problem: format!(
5863                                "`dst_image.image_type()` is `ImageType::Dim3d`, but \
5864                                `regions[{}].dst_subresource.array_layers` is not `0..1`",
5865                                region_index,
5866                            )
5867                            .into(),
5868                            vuids: &["VUID-VkBlitImageInfo2-srcImage-00240"],
5869                            ..Default::default()
5870                        }));
5871                    }
5872                }
5873            }
5874
5875            if dst_subresource.array_layers.end > dst_image.array_layers() {
5876                return Err(Box::new(ValidationError {
5877                    problem: format!(
5878                        "`regions[{}].dst_subresource.array_layers.end` is not less than \
5879                        `dst_image.array_layers()`",
5880                        region_index
5881                    )
5882                    .into(),
5883                    vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01707"],
5884                    ..Default::default()
5885                }));
5886            }
5887
5888            let dst_offsets_max = [
5889                max(dst_offsets[0][0], dst_offsets[1][0]),
5890                max(dst_offsets[0][1], dst_offsets[1][1]),
5891                max(dst_offsets[0][2], dst_offsets[1][2]),
5892            ];
5893
5894            if dst_offsets_max[0] > dst_subresource_extent[0] {
5895                return Err(Box::new(ValidationError {
5896                    problem: format!(
5897                        "`max(regions[{0}].dst_offsets[0][0], regions[{0}].dst_offsets[1][0])` is \
5898                        greater than coordinate 0 of the extent of the subresource of \
5899                        `dst_image` selected by `regions[{0}].dst_subresource`",
5900                        region_index,
5901                    )
5902                    .into(),
5903                    vuids: &["VUID-VkBlitImageInfo2-dstOffset-00248"],
5904                    ..Default::default()
5905                }));
5906            }
5907
5908            if dst_offsets_max[1] > dst_subresource_extent[1] {
5909                return Err(Box::new(ValidationError {
5910                    problem: format!(
5911                        "`max(regions[{0}].dst_offsets[0][1], regions[{0}].dst_offsets[1][1])` is \
5912                        greater than coordinate 1 of the extent of the subresource of \
5913                        `dst_image` selected by `regions[{0}].dst_subresource`",
5914                        region_index,
5915                    )
5916                    .into(),
5917                    vuids: &["VUID-VkBlitImageInfo2-dstOffset-00249"],
5918                    ..Default::default()
5919                }));
5920            }
5921
5922            if dst_offsets_max[2] > dst_subresource_extent[2] {
5923                return Err(Box::new(ValidationError {
5924                    problem: format!(
5925                        "`max(regions[{0}].dst_offsets[0][2], regions[{0}].dst_offsets[1][2])` is \
5926                        greater than coordinate 2 of the extent of the subresource of \
5927                        `dst_image` selected by `regions[{0}].dst_subresource`",
5928                        region_index,
5929                    )
5930                    .into(),
5931                    vuids: &["VUID-VkBlitImageInfo2-dstOffset-00251"],
5932                    ..Default::default()
5933                }));
5934            }
5935
5936            // VUID-VkBlitImageInfo2-pRegions-00217
5937            if is_same_image {
5938                let src_region_index = region_index;
5939                let src_subresource_axes = [
5940                    src_subresource.mip_level..src_subresource.mip_level + 1,
5941                    src_subresource.array_layers.start..src_subresource.array_layers.end,
5942                ];
5943                let src_extent_axes = [
5944                    min(src_offsets[0][0], src_offsets[1][0])
5945                        ..max(src_offsets[0][0], src_offsets[1][0]),
5946                    min(src_offsets[0][1], src_offsets[1][1])
5947                        ..max(src_offsets[0][1], src_offsets[1][1]),
5948                    min(src_offsets[0][2], src_offsets[1][2])
5949                        ..max(src_offsets[0][2], src_offsets[1][2]),
5950                ];
5951
5952                for (dst_region_index, dst_region) in regions.iter().enumerate() {
5953                    let &ImageBlit {
5954                        ref dst_subresource,
5955                        dst_offsets,
5956                        ..
5957                    } = dst_region;
5958
5959                    let dst_subresource_axes = [
5960                        dst_subresource.mip_level..dst_subresource.mip_level + 1,
5961                        src_subresource.array_layers.start..src_subresource.array_layers.end,
5962                    ];
5963
5964                    if src_subresource_axes.iter().zip(dst_subresource_axes).any(
5965                        |(src_range, dst_range)| {
5966                            src_range.start >= dst_range.end || dst_range.start >= src_range.end
5967                        },
5968                    ) {
5969                        continue;
5970                    }
5971
5972                    // If the subresource axes all overlap, then the source and destination must
5973                    // have the same layout.
5974                    overlap_subresource_indices = Some((src_region_index, dst_region_index));
5975
5976                    let dst_extent_axes = [
5977                        min(dst_offsets[0][0], dst_offsets[1][0])
5978                            ..max(dst_offsets[0][0], dst_offsets[1][0]),
5979                        min(dst_offsets[0][1], dst_offsets[1][1])
5980                            ..max(dst_offsets[0][1], dst_offsets[1][1]),
5981                        min(dst_offsets[0][2], dst_offsets[1][2])
5982                            ..max(dst_offsets[0][2], dst_offsets[1][2]),
5983                    ];
5984
5985                    if src_extent_axes
5986                        .iter()
5987                        .zip(dst_extent_axes)
5988                        .any(|(src_range, dst_range)| {
5989                            src_range.start >= dst_range.end || dst_range.start >= src_range.end
5990                        })
5991                    {
5992                        continue;
5993                    }
5994
5995                    // If the extent axes *also* overlap, then that's an error.
5996                    overlap_extent_indices = Some((src_region_index, dst_region_index));
5997                }
5998            }
5999        }
6000
6001        if let Some((src_region_index, dst_region_index)) = overlap_extent_indices {
6002            return Err(Box::new(ValidationError {
6003                problem: format!(
6004                    "`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \
6005                    overlaps with `regions[{1}].dst_subresource`, but \
6006                    the `src_offsets` of `regions[{0}]` overlaps with \
6007                    the `dst_offsets` of `regions[{1}]`",
6008                    src_region_index, dst_region_index
6009                )
6010                .into(),
6011                vuids: &["VUID-VkBlitImageInfo2-pRegions-00217"],
6012                ..Default::default()
6013            }));
6014        }
6015
6016        if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices {
6017            if src_image_layout != dst_image_layout {
6018                return Err(Box::new(ValidationError {
6019                    problem: format!(
6020                        "`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \
6021                        overlaps with `regions[{1}].dst_subresource`, but \
6022                        `src_image_layout` does not equal `dst_image_layout`",
6023                        src_region_index, dst_region_index
6024                    )
6025                    .into(),
6026                    vuids: &[
6027                        "VUID-VkBlitImageInfo2-srcImageLayout-00221",
6028                        "VUID-VkBlitImageInfo2-dstImageLayout-00226",
6029                    ],
6030                    ..Default::default()
6031                }));
6032            }
6033        }
6034
6035        Ok(())
6036    }
6037
6038    pub(crate) fn to_vk2<'a>(
6039        &self,
6040        regions_vk: &'a [ash::vk::ImageBlit2<'static>],
6041    ) -> ash::vk::BlitImageInfo2<'a> {
6042        let &Self {
6043            ref src_image,
6044            src_image_layout,
6045            ref dst_image,
6046            dst_image_layout,
6047            regions: _,
6048            filter,
6049            _ne: _,
6050        } = self;
6051
6052        ash::vk::BlitImageInfo2::default()
6053            .src_image(src_image.handle())
6054            .src_image_layout(src_image_layout.into())
6055            .dst_image(dst_image.handle())
6056            .dst_image_layout(dst_image_layout.into())
6057            .regions(regions_vk)
6058            .filter(filter.into())
6059    }
6060
6061    pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::ImageBlit2<'static>; 8]> {
6062        self.regions.iter().map(ImageBlit::to_vk2).collect()
6063    }
6064
6065    pub(crate) fn to_vk(&self) -> BlitImageInfoVk {
6066        let &Self {
6067            ref src_image,
6068            src_image_layout,
6069            ref dst_image,
6070            dst_image_layout,
6071            regions: _,
6072            filter,
6073            _ne: _,
6074        } = self;
6075
6076        BlitImageInfoVk {
6077            src_image_vk: src_image.handle(),
6078            src_image_layout_vk: src_image_layout.into(),
6079            dst_image_vk: dst_image.handle(),
6080            dst_image_layout_vk: dst_image_layout.into(),
6081            filter_vk: filter.into(),
6082        }
6083    }
6084
6085    pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::ImageBlit; 8]> {
6086        self.regions.iter().map(ImageBlit::to_vk).collect()
6087    }
6088}
6089
6090pub(crate) struct BlitImageInfoVk {
6091    pub(crate) src_image_vk: ash::vk::Image,
6092    pub(crate) src_image_layout_vk: ash::vk::ImageLayout,
6093    pub(crate) dst_image_vk: ash::vk::Image,
6094    pub(crate) dst_image_layout_vk: ash::vk::ImageLayout,
6095    pub(crate) filter_vk: ash::vk::Filter,
6096}
6097
6098/// A region of data to blit between images.
6099#[derive(Clone, Debug)]
6100pub struct ImageBlit {
6101    /// The subresource of `src_image` to blit from.
6102    ///
6103    /// The default value is empty, which must be overridden.
6104    pub src_subresource: ImageSubresourceLayers,
6105
6106    /// The offsets from the zero coordinate of `src_image`, defining two corners of the region
6107    /// to blit from.
6108    /// If the ordering of the two offsets differs between source and destination, the image will
6109    /// be flipped.
6110    ///
6111    /// The default value is `[[0; 3]; 2]`, which must be overridden.
6112    pub src_offsets: [[u32; 3]; 2],
6113
6114    /// The subresource of `dst_image` to blit to.
6115    ///
6116    /// The default value is empty, which must be overridden.
6117    pub dst_subresource: ImageSubresourceLayers,
6118
6119    /// The offset from the zero coordinate of `dst_image` defining two corners of the
6120    /// region to blit to.
6121    /// If the ordering of the two offsets differs between source and destination, the image will
6122    /// be flipped.
6123    ///
6124    /// The default value is `[[0; 3]; 2]`, which must be overridden.
6125    pub dst_offsets: [[u32; 3]; 2],
6126
6127    pub _ne: crate::NonExhaustive,
6128}
6129
6130impl Default for ImageBlit {
6131    #[inline]
6132    fn default() -> Self {
6133        Self {
6134            src_subresource: ImageSubresourceLayers {
6135                aspects: ImageAspects::empty(),
6136                mip_level: 0,
6137                array_layers: 0..0,
6138            },
6139            src_offsets: [[0; 3]; 2],
6140            dst_subresource: ImageSubresourceLayers {
6141                aspects: ImageAspects::empty(),
6142                mip_level: 0,
6143                array_layers: 0..0,
6144            },
6145            dst_offsets: [[0; 3]; 2],
6146            _ne: crate::NonExhaustive(()),
6147        }
6148    }
6149}
6150
6151impl ImageBlit {
6152    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
6153        let &Self {
6154            ref src_subresource,
6155            src_offsets: _,
6156            ref dst_subresource,
6157            dst_offsets: _,
6158            _ne: _,
6159        } = self;
6160
6161        src_subresource
6162            .validate(device)
6163            .map_err(|err| err.add_context("src_subresource"))?;
6164
6165        dst_subresource
6166            .validate(device)
6167            .map_err(|err| err.add_context("dst_subresource"))?;
6168
6169        if src_subresource.aspects != dst_subresource.aspects {
6170            return Err(Box::new(ValidationError {
6171                problem: "`src_subresource.aspects` does not equal `dst_subresource.aspects`"
6172                    .into(),
6173                vuids: &["VUID-VkImageBlit2-aspectMask-00238"],
6174                ..Default::default()
6175            }));
6176        }
6177
6178        if src_subresource.array_layers.len() != dst_subresource.array_layers.len() {
6179            return Err(Box::new(ValidationError {
6180                problem: "the length of `src_subresource.array_layers` does not equal \
6181                    the length of `dst_subresource.array_layers`"
6182                    .into(),
6183                vuids: &["VUID-VkImageBlit2-layerCount-00239"],
6184                ..Default::default()
6185            }));
6186        }
6187
6188        Ok(())
6189    }
6190
6191    pub(crate) fn to_vk2(&self) -> ash::vk::ImageBlit2<'static> {
6192        let &Self {
6193            ref src_subresource,
6194            src_offsets,
6195            ref dst_subresource,
6196            dst_offsets,
6197            _ne: _,
6198        } = self;
6199
6200        ash::vk::ImageBlit2::default()
6201            .src_subresource(src_subresource.to_vk())
6202            .src_offsets([
6203                ash::vk::Offset3D {
6204                    x: src_offsets[0][0] as i32,
6205                    y: src_offsets[0][1] as i32,
6206                    z: src_offsets[0][2] as i32,
6207                },
6208                ash::vk::Offset3D {
6209                    x: src_offsets[1][0] as i32,
6210                    y: src_offsets[1][1] as i32,
6211                    z: src_offsets[1][2] as i32,
6212                },
6213            ])
6214            .dst_subresource(dst_subresource.to_vk())
6215            .dst_offsets([
6216                ash::vk::Offset3D {
6217                    x: dst_offsets[0][0] as i32,
6218                    y: dst_offsets[0][1] as i32,
6219                    z: dst_offsets[0][2] as i32,
6220                },
6221                ash::vk::Offset3D {
6222                    x: dst_offsets[1][0] as i32,
6223                    y: dst_offsets[1][1] as i32,
6224                    z: dst_offsets[1][2] as i32,
6225                },
6226            ])
6227    }
6228
6229    pub(crate) fn to_vk(&self) -> ash::vk::ImageBlit {
6230        let &Self {
6231            ref src_subresource,
6232            src_offsets,
6233            ref dst_subresource,
6234            dst_offsets,
6235            _ne: _,
6236        } = self;
6237
6238        ash::vk::ImageBlit {
6239            src_subresource: src_subresource.to_vk(),
6240            src_offsets: [
6241                ash::vk::Offset3D {
6242                    x: src_offsets[0][0] as i32,
6243                    y: src_offsets[0][1] as i32,
6244                    z: src_offsets[0][2] as i32,
6245                },
6246                ash::vk::Offset3D {
6247                    x: src_offsets[1][0] as i32,
6248                    y: src_offsets[1][1] as i32,
6249                    z: src_offsets[1][2] as i32,
6250                },
6251            ],
6252            dst_subresource: dst_subresource.to_vk(),
6253            dst_offsets: [
6254                ash::vk::Offset3D {
6255                    x: dst_offsets[0][0] as i32,
6256                    y: dst_offsets[0][1] as i32,
6257                    z: dst_offsets[0][2] as i32,
6258                },
6259                ash::vk::Offset3D {
6260                    x: dst_offsets[1][0] as i32,
6261                    y: dst_offsets[1][1] as i32,
6262                    z: dst_offsets[1][2] as i32,
6263                },
6264            ],
6265        }
6266    }
6267}
6268
6269/// Parameters to resolve image data.
6270#[derive(Clone, Debug)]
6271pub struct ResolveImageInfo {
6272    /// The multisampled image to resolve from.
6273    ///
6274    /// There is no default value.
6275    pub src_image: Arc<Image>,
6276
6277    /// The layout used for `src_image` during the resolve operation.
6278    ///
6279    /// The following layouts are allowed:
6280    /// - [`ImageLayout::TransferSrcOptimal`]
6281    /// - [`ImageLayout::General`]
6282    ///
6283    /// The default value is [`ImageLayout::TransferSrcOptimal`].
6284    pub src_image_layout: ImageLayout,
6285
6286    /// The non-multisampled image to resolve into.
6287    ///
6288    /// There is no default value.
6289    pub dst_image: Arc<Image>,
6290
6291    /// The layout used for `dst_image` during the resolve operation.
6292    ///
6293    /// The following layouts are allowed:
6294    /// - [`ImageLayout::TransferDstOptimal`]
6295    /// - [`ImageLayout::General`]
6296    ///
6297    /// The default value is [`ImageLayout::TransferDstOptimal`].
6298    pub dst_image_layout: ImageLayout,
6299
6300    /// The regions of both images to resolve between.
6301    ///
6302    /// The default value is a single region, covering the first mip level, and the smallest of the
6303    /// array layers and extent of the two images. All aspects of each image are selected, or
6304    /// `plane0` if the image is multi-planar.
6305    pub regions: SmallVec<[ImageResolve; 1]>,
6306
6307    pub _ne: crate::NonExhaustive,
6308}
6309
6310impl ResolveImageInfo {
6311    /// Returns a `ResolveImageInfo` with the specified `src_image` and `dst_image`.
6312    #[inline]
6313    pub fn images(src_image: Arc<Image>, dst_image: Arc<Image>) -> Self {
6314        let min_array_layers = src_image.array_layers().min(dst_image.array_layers());
6315        let region = ImageResolve {
6316            src_subresource: ImageSubresourceLayers {
6317                array_layers: 0..min_array_layers,
6318                ..src_image.subresource_layers()
6319            },
6320            dst_subresource: ImageSubresourceLayers {
6321                array_layers: 0..min_array_layers,
6322                ..dst_image.subresource_layers()
6323            },
6324            extent: {
6325                let src_extent = src_image.extent();
6326                let dst_extent = dst_image.extent();
6327
6328                [
6329                    src_extent[0].min(dst_extent[0]),
6330                    src_extent[1].min(dst_extent[1]),
6331                    src_extent[2].min(dst_extent[2]),
6332                ]
6333            },
6334            ..Default::default()
6335        };
6336
6337        Self {
6338            src_image,
6339            src_image_layout: ImageLayout::TransferSrcOptimal,
6340            dst_image,
6341            dst_image_layout: ImageLayout::TransferDstOptimal,
6342            regions: smallvec![region],
6343            _ne: crate::NonExhaustive(()),
6344        }
6345    }
6346
6347    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
6348        let &Self {
6349            ref src_image,
6350            src_image_layout,
6351            ref dst_image,
6352            dst_image_layout,
6353            ref regions,
6354            _ne: _,
6355        } = self;
6356
6357        src_image_layout.validate_device(device).map_err(|err| {
6358            err.add_context("src_image_layout")
6359                .set_vuids(&["VUID-VkResolveImageInfo2-srcImageLayout-parameter"])
6360        })?;
6361
6362        dst_image_layout.validate_device(device).map_err(|err| {
6363            err.add_context("dst_image_layout")
6364                .set_vuids(&["VUID-VkResolveImageInfo2-dstImageLayout-parameter"])
6365        })?;
6366
6367        // VUID-VkResolveImageInfo2-commonparent
6368        assert_eq!(device, src_image.device().as_ref());
6369        assert_eq!(device, dst_image.device().as_ref());
6370
6371        let src_image_format = src_image.format();
6372        let dst_image_format = dst_image.format();
6373
6374        if src_image.samples() == SampleCount::Sample1 {
6375            return Err(Box::new(ValidationError {
6376                context: "src_image.samples()".into(),
6377                problem: "is `SampleCount::Sample1`".into(),
6378                vuids: &["VUID-VkResolveImageInfo2-srcImage-00257"],
6379                ..Default::default()
6380            }));
6381        }
6382
6383        if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) {
6384            return Err(Box::new(ValidationError {
6385                context: "src_image.usage()".into(),
6386                problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(),
6387                vuids: &["VUID-VkResolveImageInfo2-srcImage-06762"],
6388                ..Default::default()
6389            }));
6390        }
6391
6392        if !src_image
6393            .format_features()
6394            .intersects(FormatFeatures::TRANSFER_SRC)
6395        {
6396            return Err(Box::new(ValidationError {
6397                context: "src_image.format_features()".into(),
6398                problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(),
6399                vuids: &["VUID-VkResolveImageInfo2-srcImage-06763"],
6400                ..Default::default()
6401            }));
6402        }
6403
6404        if !matches!(
6405            src_image_layout,
6406            ImageLayout::TransferSrcOptimal | ImageLayout::General
6407        ) {
6408            return Err(Box::new(ValidationError {
6409                context: "src_image_layout".into(),
6410                problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`"
6411                    .into(),
6412                vuids: &["VUID-VkResolveImageInfo2-srcImageLayout-01400"],
6413                ..Default::default()
6414            }));
6415        }
6416
6417        if dst_image.samples() != SampleCount::Sample1 {
6418            return Err(Box::new(ValidationError {
6419                context: "dst_image.samples()".into(),
6420                problem: "is not `SampleCount::Sample1`".into(),
6421                vuids: &["VUID-VkResolveImageInfo2-dstImage-00259"],
6422                ..Default::default()
6423            }));
6424        }
6425
6426        if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) {
6427            return Err(Box::new(ValidationError {
6428                context: "dst_image.usage()".into(),
6429                problem: "does not contain `ImageUsage::TRANSFER_DST`".into(),
6430                vuids: &["VUID-VkResolveImageInfo2-dstImage-06764"],
6431                ..Default::default()
6432            }));
6433        }
6434
6435        if !dst_image
6436            .format_features()
6437            .contains(FormatFeatures::TRANSFER_DST | FormatFeatures::COLOR_ATTACHMENT)
6438        {
6439            return Err(Box::new(ValidationError {
6440                context: "dst_image.format_features()".into(),
6441                problem: "does not contain both `FormatFeatures::TRANSFER_DST` and \
6442                    `FormatFeatures::COLOR_ATTACHMENT`"
6443                    .into(),
6444                vuids: &[
6445                    "VUID-VkResolveImageInfo2-dstImage-06765",
6446                    "VUID-VkResolveImageInfo2-dstImage-02003",
6447                ],
6448                ..Default::default()
6449            }));
6450        }
6451
6452        if device.enabled_features().linear_color_attachment
6453            && dst_image.tiling() == ImageTiling::Linear
6454            && !dst_image
6455                .format_features()
6456                .contains(FormatFeatures::LINEAR_COLOR_ATTACHMENT)
6457        {
6458            return Err(Box::new(ValidationError {
6459                problem: "the `linear_color_attachment` feature is enabled on the device, and \
6460                    `dst_image.tiling()` is `ImageTiling::Linear`, but \
6461                    `dst_image.format_features()` does not contain \
6462                    `FormatFeatures::LINEAR_COLOR_ATTACHMENT`"
6463                    .into(),
6464                vuids: &["VUID-VkResolveImageInfo2-linearColorAttachment-06519"],
6465                ..Default::default()
6466            }));
6467        }
6468
6469        if !matches!(
6470            dst_image_layout,
6471            ImageLayout::TransferDstOptimal | ImageLayout::General
6472        ) {
6473            return Err(Box::new(ValidationError {
6474                context: "dst_image_layout".into(),
6475                problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
6476                    .into(),
6477                vuids: &["VUID-VkResolveImageInfo2-dstImageLayout-01401"],
6478                ..Default::default()
6479            }));
6480        }
6481
6482        if src_image_format != dst_image_format {
6483            return Err(Box::new(ValidationError {
6484                problem: "`src_image.format()` does not equal `dst_image.format()`".into(),
6485                vuids: &["VUID-VkResolveImageInfo2-srcImage-01386"],
6486                ..Default::default()
6487            }));
6488        }
6489
6490        for (region_index, region) in regions.iter().enumerate() {
6491            region
6492                .validate(device)
6493                .map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
6494
6495            let &ImageResolve {
6496                ref src_subresource,
6497                src_offset,
6498                ref dst_subresource,
6499                dst_offset,
6500                extent,
6501                _ne: _,
6502            } = region;
6503
6504            /*
6505               Check src
6506            */
6507
6508            if src_subresource.mip_level >= src_image.mip_levels() {
6509                return Err(Box::new(ValidationError {
6510                    problem: format!(
6511                        "`regions[{}].src_subresource.mip_level` is not less than \
6512                        `src_image.mip_levels()`",
6513                        region_index
6514                    )
6515                    .into(),
6516                    vuids: &["VUID-VkResolveImageInfo2-srcSubresource-01709"],
6517                    ..Default::default()
6518                }));
6519            }
6520
6521            let src_subresource_extent =
6522                mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap();
6523
6524            match src_image.image_type() {
6525                ImageType::Dim1d => {
6526                    if src_offset[1] != 0 {
6527                        return Err(Box::new(ValidationError {
6528                            problem: format!(
6529                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
6530                                `regions[{}].src_offset[1]` is not 0",
6531                                region_index,
6532                            )
6533                            .into(),
6534                            vuids: &["VUID-VkResolveImageInfo2-srcImage-00271"],
6535                            ..Default::default()
6536                        }));
6537                    }
6538
6539                    if extent[1] != 1 {
6540                        return Err(Box::new(ValidationError {
6541                            problem: format!(
6542                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
6543                                `regions[{}].extent[1]` is not 1",
6544                                region_index,
6545                            )
6546                            .into(),
6547                            vuids: &["VUID-VkResolveImageInfo2-srcImage-00271"],
6548                            ..Default::default()
6549                        }));
6550                    }
6551
6552                    if src_offset[2] != 0 {
6553                        return Err(Box::new(ValidationError {
6554                            problem: format!(
6555                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
6556                                `regions[{}].src_offset[2]` is not 0",
6557                                region_index,
6558                            )
6559                            .into(),
6560                            vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"],
6561                            ..Default::default()
6562                        }));
6563                    }
6564
6565                    if extent[2] != 1 {
6566                        return Err(Box::new(ValidationError {
6567                            problem: format!(
6568                                "`src_image.image_type()` is `ImageType::Dim1d`, but \
6569                                `regions[{}].extent[2]` is not 1",
6570                                region_index,
6571                            )
6572                            .into(),
6573                            vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"],
6574                            ..Default::default()
6575                        }));
6576                    }
6577                }
6578                ImageType::Dim2d => {
6579                    if src_offset[2] != 0 {
6580                        return Err(Box::new(ValidationError {
6581                            problem: format!(
6582                                "`src_image.image_type()` is `ImageType::Dim2d`, but \
6583                                `regions[{}].src_offset[2]` is not 0",
6584                                region_index,
6585                            )
6586                            .into(),
6587                            vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"],
6588                            ..Default::default()
6589                        }));
6590                    }
6591
6592                    if extent[2] != 1 {
6593                        return Err(Box::new(ValidationError {
6594                            problem: format!(
6595                                "`src_image.image_type()` is `ImageType::Dim2d`, but \
6596                                `regions[{}].extent[2]` is not 1",
6597                                region_index,
6598                            )
6599                            .into(),
6600                            vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"],
6601                            ..Default::default()
6602                        }));
6603                    }
6604                }
6605                ImageType::Dim3d => {
6606                    if src_subresource.array_layers != (0..1) {
6607                        return Err(Box::new(ValidationError {
6608                            problem: format!(
6609                                "`src_image.image_type()` is `ImageType::Dim3d`, but \
6610                                `regions[{}].src_subresource.array_layers` is not `0..1`",
6611                                region_index,
6612                            )
6613                            .into(),
6614                            vuids: &["VUID-VkResolveImageInfo2-srcImage-04446"],
6615                            ..Default::default()
6616                        }));
6617                    }
6618                }
6619            }
6620
6621            if src_subresource.array_layers.end > src_image.array_layers() {
6622                return Err(Box::new(ValidationError {
6623                    problem: format!(
6624                        "`regions[{}].src_subresource.array_layers.end` is not less than \
6625                        `src_image.array_layers()`",
6626                        region_index
6627                    )
6628                    .into(),
6629                    vuids: &["VUID-VkResolveImageInfo2-srcSubresource-01711"],
6630                    ..Default::default()
6631                }));
6632            }
6633
6634            if src_offset[0] + extent[0] > src_subresource_extent[0] {
6635                return Err(Box::new(ValidationError {
6636                    problem: format!(
6637                        "`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is greater \
6638                        than coordinate 0 of the extent of the subresource of `src_image` \
6639                        selected by `regions[{0}].src_subresource`",
6640                        region_index,
6641                    )
6642                    .into(),
6643                    vuids: &["VUID-VkResolveImageInfo2-srcOffset-00269"],
6644                    ..Default::default()
6645                }));
6646            }
6647
6648            if src_offset[1] + extent[1] > src_subresource_extent[1] {
6649                return Err(Box::new(ValidationError {
6650                    problem: format!(
6651                        "`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is greater \
6652                        than coordinate 1 of the extent of the subresource of `src_image` \
6653                        selected by `regions[{0}].src_subresource`",
6654                        region_index,
6655                    )
6656                    .into(),
6657                    vuids: &["VUID-VkResolveImageInfo2-srcOffset-00270"],
6658                    ..Default::default()
6659                }));
6660            }
6661
6662            if src_offset[2] + extent[2] > src_subresource_extent[2] {
6663                return Err(Box::new(ValidationError {
6664                    problem: format!(
6665                        "`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is greater \
6666                        than coordinate 2 of the extent of the subresource of `src_image` \
6667                        selected by `regions[{0}].src_subresource`",
6668                        region_index,
6669                    )
6670                    .into(),
6671                    vuids: &["VUID-VkResolveImageInfo2-srcOffset-00272"],
6672                    ..Default::default()
6673                }));
6674            }
6675
6676            /*
6677               Check dst
6678            */
6679
6680            if dst_subresource.mip_level >= dst_image.mip_levels() {
6681                return Err(Box::new(ValidationError {
6682                    problem: format!(
6683                        "`regions[{}].dst_subresource.mip_level` is not less than \
6684                        `dst_image.mip_levels()`",
6685                        region_index
6686                    )
6687                    .into(),
6688                    vuids: &["VUID-VkResolveImageInfo2-dstSubresource-01710"],
6689                    ..Default::default()
6690                }));
6691            }
6692
6693            let dst_subresource_extent =
6694                mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap();
6695
6696            match dst_image.image_type() {
6697                ImageType::Dim1d => {
6698                    if dst_offset[1] != 0 {
6699                        return Err(Box::new(ValidationError {
6700                            problem: format!(
6701                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
6702                                `regions[{}].dst_offset[1]` is not 0",
6703                                region_index,
6704                            )
6705                            .into(),
6706                            vuids: &["VUID-VkResolveImageInfo2-dstImage-00276"],
6707                            ..Default::default()
6708                        }));
6709                    }
6710
6711                    if extent[1] != 1 {
6712                        return Err(Box::new(ValidationError {
6713                            problem: format!(
6714                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
6715                                `regions[{}].extent[1]` is not 1",
6716                                region_index,
6717                            )
6718                            .into(),
6719                            vuids: &["VUID-VkResolveImageInfo2-dstImage-00276"],
6720                            ..Default::default()
6721                        }));
6722                    }
6723
6724                    if dst_offset[2] != 0 {
6725                        return Err(Box::new(ValidationError {
6726                            problem: format!(
6727                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
6728                                `regions[{}].dst_offset[2]` is not 0",
6729                                region_index,
6730                            )
6731                            .into(),
6732                            vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"],
6733                            ..Default::default()
6734                        }));
6735                    }
6736
6737                    if extent[2] != 1 {
6738                        return Err(Box::new(ValidationError {
6739                            problem: format!(
6740                                "`dst_image.image_type()` is `ImageType::Dim1d`, but \
6741                                `regions[{}].extent[2]` is not 1",
6742                                region_index,
6743                            )
6744                            .into(),
6745                            vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"],
6746                            ..Default::default()
6747                        }));
6748                    }
6749                }
6750                ImageType::Dim2d => {
6751                    if dst_offset[2] != 0 {
6752                        return Err(Box::new(ValidationError {
6753                            problem: format!(
6754                                "`dst_image.image_type()` is `ImageType::Dim2d`, but \
6755                                `regions[{}].dst_offset[2]` is not 0",
6756                                region_index,
6757                            )
6758                            .into(),
6759                            vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"],
6760                            ..Default::default()
6761                        }));
6762                    }
6763
6764                    if extent[2] != 1 {
6765                        return Err(Box::new(ValidationError {
6766                            problem: format!(
6767                                "`dst_image.image_type()` is `ImageType::Dim2d`, but \
6768                                `regions[{}].extent[2]` is not 1",
6769                                region_index,
6770                            )
6771                            .into(),
6772                            vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"],
6773                            ..Default::default()
6774                        }));
6775                    }
6776                }
6777                ImageType::Dim3d => {
6778                    if dst_subresource.array_layers != (0..1) {
6779                        return Err(Box::new(ValidationError {
6780                            problem: format!(
6781                                "`dst_image.image_type()` is `ImageType::Dim3d`, but \
6782                                `regions[{}].dst_subresource.array_layers` is not `0..1`",
6783                                region_index,
6784                            )
6785                            .into(),
6786                            vuids: &["VUID-VkResolveImageInfo2-srcImage-04447"],
6787                            ..Default::default()
6788                        }));
6789                    }
6790                }
6791            }
6792
6793            if dst_subresource.array_layers.end > dst_image.array_layers() {
6794                return Err(Box::new(ValidationError {
6795                    problem: format!(
6796                        "`regions[{}].dst_subresource.array_layers.end` is not less than \
6797                        `dst_image.array_layers()`",
6798                        region_index
6799                    )
6800                    .into(),
6801                    vuids: &["VUID-VkResolveImageInfo2-dstSubresource-01712"],
6802                    ..Default::default()
6803                }));
6804            }
6805
6806            if dst_offset[0] + extent[0] > dst_subresource_extent[0] {
6807                return Err(Box::new(ValidationError {
6808                    problem: format!(
6809                        "`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is greater \
6810                        than coordinate 0 of the extent of the subresource of `dst_image` \
6811                        selected by `regions[{0}].dst_subresource`",
6812                        region_index,
6813                    )
6814                    .into(),
6815                    vuids: &["VUID-VkResolveImageInfo2-dstOffset-00274"],
6816                    ..Default::default()
6817                }));
6818            }
6819
6820            if dst_offset[1] + extent[1] > dst_subresource_extent[1] {
6821                return Err(Box::new(ValidationError {
6822                    problem: format!(
6823                        "`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is greater \
6824                        than coordinate 1 of the extent of the subresource of `dst_image` \
6825                        selected by `regions[{0}].dst_subresource`",
6826                        region_index,
6827                    )
6828                    .into(),
6829                    vuids: &["VUID-VkResolveImageInfo2-dstOffset-00275"],
6830                    ..Default::default()
6831                }));
6832            }
6833
6834            if dst_offset[2] + extent[2] > dst_subresource_extent[2] {
6835                return Err(Box::new(ValidationError {
6836                    problem: format!(
6837                        "`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is greater \
6838                        than coordinate 2 of the extent of the subresource of `dst_image` \
6839                        selected by `regions[{0}].dst_subresource`",
6840                        region_index,
6841                    )
6842                    .into(),
6843                    vuids: &["VUID-VkResolveImageInfo2-dstOffset-00277"],
6844                    ..Default::default()
6845                }));
6846            }
6847        }
6848
6849        // VUID-VkResolveImageInfo2-pRegions-00255
6850        // Can't occur as long as memory aliasing isn't allowed, because `src_image` and
6851        // `dst_image` must have different sample counts and therefore can never be the same image.
6852
6853        Ok(())
6854    }
6855
6856    pub(crate) fn to_vk2<'a>(
6857        &self,
6858        regions_vk: &'a [ash::vk::ImageResolve2<'static>],
6859    ) -> ash::vk::ResolveImageInfo2<'a> {
6860        let &Self {
6861            ref src_image,
6862            src_image_layout,
6863            ref dst_image,
6864            dst_image_layout,
6865            regions: _,
6866            _ne: _,
6867        } = self;
6868
6869        ash::vk::ResolveImageInfo2::default()
6870            .src_image(src_image.handle())
6871            .src_image_layout(src_image_layout.into())
6872            .dst_image(dst_image.handle())
6873            .dst_image_layout(dst_image_layout.into())
6874            .regions(regions_vk)
6875    }
6876
6877    pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::ImageResolve2<'static>; 8]> {
6878        self.regions.iter().map(ImageResolve::to_vk2).collect()
6879    }
6880
6881    pub(crate) fn to_vk(&self) -> ResolveImageInfoVk {
6882        let &Self {
6883            ref src_image,
6884            src_image_layout,
6885            ref dst_image,
6886            dst_image_layout,
6887            regions: _,
6888            _ne: _,
6889        } = self;
6890
6891        ResolveImageInfoVk {
6892            src_image_vk: src_image.handle(),
6893            src_image_layout_vk: src_image_layout.into(),
6894            dst_image_vk: dst_image.handle(),
6895            dst_image_layout_vk: dst_image_layout.into(),
6896        }
6897    }
6898
6899    pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::ImageResolve; 8]> {
6900        self.regions.iter().map(ImageResolve::to_vk).collect()
6901    }
6902}
6903
6904pub(crate) struct ResolveImageInfoVk {
6905    pub(crate) src_image_vk: ash::vk::Image,
6906    pub(crate) src_image_layout_vk: ash::vk::ImageLayout,
6907    pub(crate) dst_image_vk: ash::vk::Image,
6908    pub(crate) dst_image_layout_vk: ash::vk::ImageLayout,
6909}
6910
6911/// A region of data to resolve between images.
6912#[derive(Clone, Debug)]
6913pub struct ImageResolve {
6914    /// The subresource of `src_image` to resolve from.
6915    ///
6916    /// The default value is empty, which must be overridden.
6917    pub src_subresource: ImageSubresourceLayers,
6918
6919    /// The offset from the zero coordinate of `src_image` that resolving will start from.
6920    ///
6921    /// The default value is `[0; 3]`.
6922    pub src_offset: [u32; 3],
6923
6924    /// The subresource of `dst_image` to resolve into.
6925    ///
6926    /// The default value is empty, which must be overridden.
6927    pub dst_subresource: ImageSubresourceLayers,
6928
6929    /// The offset from the zero coordinate of `dst_image` that resolving will start from.
6930    ///
6931    /// The default value is `[0; 3]`.
6932    pub dst_offset: [u32; 3],
6933
6934    /// The extent of texels to resolve.
6935    ///
6936    /// The default value is `[0; 3]`, which must be overridden.
6937    pub extent: [u32; 3],
6938
6939    pub _ne: crate::NonExhaustive,
6940}
6941
6942impl Default for ImageResolve {
6943    #[inline]
6944    fn default() -> Self {
6945        Self {
6946            src_subresource: ImageSubresourceLayers {
6947                aspects: ImageAspects::empty(),
6948                mip_level: 0,
6949                array_layers: 0..0,
6950            },
6951            src_offset: [0; 3],
6952            dst_subresource: ImageSubresourceLayers {
6953                aspects: ImageAspects::empty(),
6954                mip_level: 0,
6955                array_layers: 0..0,
6956            },
6957            dst_offset: [0; 3],
6958            extent: [0; 3],
6959            _ne: crate::NonExhaustive(()),
6960        }
6961    }
6962}
6963
6964impl ImageResolve {
6965    pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
6966        let &Self {
6967            ref src_subresource,
6968            src_offset: _,
6969            ref dst_subresource,
6970            dst_offset: _,
6971            extent: _,
6972            _ne: _,
6973        } = self;
6974
6975        src_subresource
6976            .validate(device)
6977            .map_err(|err| err.add_context("src_subresource"))?;
6978
6979        dst_subresource
6980            .validate(device)
6981            .map_err(|err| err.add_context("dst_subresource"))?;
6982
6983        if src_subresource.aspects != ImageAspects::COLOR {
6984            return Err(Box::new(ValidationError {
6985                problem: "`src_subresource.aspects` is not `ImageAspects::COLOR`".into(),
6986                vuids: &["VUID-VkImageResolve2-aspectMask-00266"],
6987                ..Default::default()
6988            }));
6989        }
6990
6991        if dst_subresource.aspects != ImageAspects::COLOR {
6992            return Err(Box::new(ValidationError {
6993                problem: "`dst_subresource.aspects` is not `ImageAspects::COLOR`".into(),
6994                vuids: &["VUID-VkImageResolve2-aspectMask-00266"],
6995                ..Default::default()
6996            }));
6997        }
6998
6999        if src_subresource.array_layers.len() != dst_subresource.array_layers.len() {
7000            return Err(Box::new(ValidationError {
7001                problem: "the length of `src_subresource.array_layers` does not equal \
7002                    the length of `dst_subresource.array_layers`"
7003                    .into(),
7004                vuids: &["VUID-VkImageResolve2-layerCount-00267"],
7005                ..Default::default()
7006            }));
7007        }
7008
7009        Ok(())
7010    }
7011
7012    pub(crate) fn to_vk2(&self) -> ash::vk::ImageResolve2<'static> {
7013        let &Self {
7014            ref src_subresource,
7015            src_offset,
7016            ref dst_subresource,
7017            dst_offset,
7018            extent,
7019            _ne: _,
7020        } = self;
7021
7022        ash::vk::ImageResolve2::default()
7023            .src_subresource(src_subresource.to_vk())
7024            .src_offset(ash::vk::Offset3D {
7025                x: src_offset[0] as i32,
7026                y: src_offset[1] as i32,
7027                z: src_offset[2] as i32,
7028            })
7029            .dst_subresource(dst_subresource.to_vk())
7030            .dst_offset(ash::vk::Offset3D {
7031                x: dst_offset[0] as i32,
7032                y: dst_offset[1] as i32,
7033                z: dst_offset[2] as i32,
7034            })
7035            .extent(ash::vk::Extent3D {
7036                width: extent[0],
7037                height: extent[1],
7038                depth: extent[2],
7039            })
7040    }
7041
7042    pub(crate) fn to_vk(&self) -> ash::vk::ImageResolve {
7043        let &Self {
7044            ref src_subresource,
7045            src_offset,
7046            ref dst_subresource,
7047            dst_offset,
7048            extent,
7049            _ne: _,
7050        } = self;
7051
7052        ash::vk::ImageResolve {
7053            src_subresource: src_subresource.to_vk(),
7054            src_offset: ash::vk::Offset3D {
7055                x: src_offset[0] as i32,
7056                y: src_offset[1] as i32,
7057                z: src_offset[2] as i32,
7058            },
7059            dst_subresource: dst_subresource.to_vk(),
7060            dst_offset: ash::vk::Offset3D {
7061                x: dst_offset[0] as i32,
7062                y: dst_offset[1] as i32,
7063                z: dst_offset[2] as i32,
7064            },
7065            extent: ash::vk::Extent3D {
7066                width: extent[0],
7067                height: extent[1],
7068                depth: extent[2],
7069            },
7070        }
7071    }
7072}
7073
7074#[cfg(test)]
7075mod tests {
7076    use super::*;
7077    use crate::format::Format;
7078
7079    /// Computes the minimum required len in elements for buffer with image data in specified
7080    /// format of specified size.
7081    fn required_size_for_format(format: Format, extent: [u32; 3], layer_count: u32) -> DeviceSize {
7082        let num_blocks = extent
7083            .into_iter()
7084            .zip(format.block_extent())
7085            .map(|(extent, block_extent)| {
7086                let extent = extent as DeviceSize;
7087                let block_extent = block_extent as DeviceSize;
7088                extent.div_ceil(block_extent)
7089            })
7090            .product::<DeviceSize>()
7091            * layer_count as DeviceSize;
7092
7093        num_blocks * format.block_size()
7094    }
7095
7096    #[test]
7097    fn test_required_len_for_format() {
7098        // issue #1292
7099        assert_eq!(
7100            required_size_for_format(Format::BC1_RGB_UNORM_BLOCK, [2048, 2048, 1], 1),
7101            2097152
7102        );
7103        // other test cases
7104        assert_eq!(
7105            required_size_for_format(Format::R8G8B8A8_UNORM, [2048, 2048, 1], 1),
7106            16777216
7107        );
7108        assert_eq!(
7109            required_size_for_format(Format::R4G4_UNORM_PACK8, [512, 512, 1], 1),
7110            262144
7111        );
7112        assert_eq!(
7113            required_size_for_format(Format::R8G8B8_USCALED, [512, 512, 1], 1),
7114            786432
7115        );
7116        assert_eq!(
7117            required_size_for_format(Format::R32G32_UINT, [512, 512, 1], 1),
7118            2097152
7119        );
7120        assert_eq!(
7121            required_size_for_format(Format::R32G32_UINT, [512, 512, 1], 1),
7122            2097152
7123        );
7124        assert_eq!(
7125            required_size_for_format(Format::ASTC_8x8_UNORM_BLOCK, [512, 512, 1], 1),
7126            65536
7127        );
7128        assert_eq!(
7129            required_size_for_format(Format::ASTC_12x12_SRGB_BLOCK, [512, 512, 1], 1),
7130            29584
7131        );
7132    }
7133}