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
22impl<L> AutoCommandBufferBuilder<L> {
24 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(©_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 } = ©_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(©_buffer_info) };
103 },
104 );
105
106 self
107 }
108
109 pub fn copy_image(
129 &mut self,
130 copy_image_info: CopyImageInfo,
131 ) -> Result<&mut Self, Box<ValidationError>> {
132 self.validate_copy_image(©_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 } = ©_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(©_image_info) };
205 },
206 );
207
208 self
209 }
210
211 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(©_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 } = ©_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(©_buffer_to_image_info) };
292 },
293 );
294
295 self
296 }
297
298 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(©_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 } = ©_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(©_image_to_buffer_info) };
379 },
380 );
381
382 self
383 }
384
385 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 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(®ions_vk);
639
640 if self.device().api_version() >= Version::V1_3 {
641 unsafe { (fns.v1_3.cmd_copy_buffer2)(self.handle(), ©_buffer_info_vk) };
642 } else {
643 unsafe {
644 (fns.khr_copy_commands2.cmd_copy_buffer2_khr)(
645 self.handle(),
646 ©_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 (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
731 (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
733 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 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 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 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 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(®ions_vk);
989
990 if self.device().api_version() >= Version::V1_3 {
991 unsafe { (fns.v1_3.cmd_copy_image2)(self.handle(), ©_image_info_vk) };
992 } else {
993 unsafe {
994 (fns.khr_copy_commands2.cmd_copy_image2_khr)(self.handle(), ©_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 (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
1074 (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
1076 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(®ions_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 ©_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 ©_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 (queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
1354 (queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
1356 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(®ions_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 ©_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 ©_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(®ions_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(®ions_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#[derive(Clone, Debug)]
1736pub struct CopyBufferInfo {
1737 pub src_buffer: Subbuffer<[u8]>,
1741
1742 pub dst_buffer: Subbuffer<[u8]>,
1746
1747 pub regions: SmallVec<[BufferCopy; 1]>,
1752
1753 pub _ne: crate::NonExhaustive,
1754}
1755
1756impl CopyBufferInfo {
1757 #[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 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 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 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#[derive(Clone, Debug)]
1977pub struct CopyBufferInfoTyped<T> {
1978 pub src_buffer: Subbuffer<[T]>,
1982
1983 pub dst_buffer: Subbuffer<[T]>,
1987
1988 pub regions: SmallVec<[BufferCopy; 1]>,
1993
1994 pub _ne: crate::NonExhaustive,
1995}
1996
1997impl<T> CopyBufferInfoTyped<T> {
1998 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#[derive(Clone, Debug)]
2040pub struct BufferCopy {
2041 pub src_offset: DeviceSize,
2046
2047 pub dst_offset: DeviceSize,
2052
2053 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#[derive(Clone, Debug)]
2126pub struct CopyImageInfo {
2127 pub src_image: Arc<Image>,
2131
2132 pub src_image_layout: ImageLayout,
2140
2141 pub dst_image: Arc<Image>,
2145
2146 pub dst_image_layout: ImageLayout,
2154
2155 pub regions: SmallVec<[ImageCopy; 1]>,
2161
2162 pub _ne: crate::NonExhaustive,
2163}
2164
2165impl CopyImageInfo {
2166 #[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 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 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 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 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 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 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 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 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#[derive(Clone, Debug)]
3438pub struct ImageCopy {
3439 pub src_subresource: ImageSubresourceLayers,
3443
3444 pub src_offset: [u32; 3],
3448
3449 pub dst_subresource: ImageSubresourceLayers,
3453
3454 pub dst_offset: [u32; 3],
3458
3459 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#[derive(Clone, Debug)]
3627pub struct CopyBufferToImageInfo {
3628 pub src_buffer: Subbuffer<[u8]>,
3632
3633 pub dst_image: Arc<Image>,
3637
3638 pub dst_image_layout: ImageLayout,
3646
3647 pub regions: SmallVec<[BufferImageCopy; 1]>,
3652
3653 pub _ne: crate::NonExhaustive,
3654}
3655
3656impl CopyBufferToImageInfo {
3657 #[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 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 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 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 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#[derive(Clone, Debug)]
4311pub struct CopyImageToBufferInfo {
4312 pub src_image: Arc<Image>,
4316
4317 pub src_image_layout: ImageLayout,
4325
4326 pub dst_buffer: Subbuffer<[u8]>,
4330
4331 pub regions: SmallVec<[BufferImageCopy; 1]>,
4336
4337 pub _ne: crate::NonExhaustive,
4338}
4339
4340impl CopyImageToBufferInfo {
4341 #[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 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 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 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 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#[derive(Clone, Debug)]
4992pub struct BufferImageCopy {
4993 pub buffer_offset: DeviceSize,
4997
4998 pub buffer_row_length: u32,
5004
5005 pub buffer_image_height: u32,
5011
5012 pub image_subresource: ImageSubresourceLayers,
5016
5017 pub image_offset: [u32; 3],
5021
5022 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 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 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 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#[derive(Clone, Debug)]
5230pub struct BlitImageInfo {
5231 pub src_image: Arc<Image>,
5235
5236 pub src_image_layout: ImageLayout,
5244
5245 pub dst_image: Arc<Image>,
5249
5250 pub dst_image_layout: ImageLayout,
5258
5259 pub regions: SmallVec<[ImageBlit; 1]>,
5266
5267 pub filter: Filter,
5272
5273 pub _ne: crate::NonExhaustive,
5274}
5275
5276impl BlitImageInfo {
5277 #[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 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 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 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 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 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 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#[derive(Clone, Debug)]
6100pub struct ImageBlit {
6101 pub src_subresource: ImageSubresourceLayers,
6105
6106 pub src_offsets: [[u32; 3]; 2],
6113
6114 pub dst_subresource: ImageSubresourceLayers,
6118
6119 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#[derive(Clone, Debug)]
6271pub struct ResolveImageInfo {
6272 pub src_image: Arc<Image>,
6276
6277 pub src_image_layout: ImageLayout,
6285
6286 pub dst_image: Arc<Image>,
6290
6291 pub dst_image_layout: ImageLayout,
6299
6300 pub regions: SmallVec<[ImageResolve; 1]>,
6306
6307 pub _ne: crate::NonExhaustive,
6308}
6309
6310impl ResolveImageInfo {
6311 #[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 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 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 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 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#[derive(Clone, Debug)]
6913pub struct ImageResolve {
6914 pub src_subresource: ImageSubresourceLayers,
6918
6919 pub src_offset: [u32; 3],
6923
6924 pub dst_subresource: ImageSubresourceLayers,
6928
6929 pub dst_offset: [u32; 3],
6933
6934 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 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 assert_eq!(
7100 required_size_for_format(Format::BC1_RGB_UNORM_BLOCK, [2048, 2048, 1], 1),
7101 2097152
7102 );
7103 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}