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