| // Copyright (c) 2022 The vulkano developers |
| // Licensed under the Apache License, Version 2.0 |
| // <LICENSE-APACHE or |
| // https://www.apache.org/licenses/LICENSE-2.0> or the MIT |
| // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, |
| // at your option. All files in the project carrying such |
| // notice may not be copied, modified, or distributed except |
| // according to those terms. |
| |
| use crate::{ |
| buffer::{BufferUsage, Subbuffer}, |
| command_buffer::{ |
| allocator::CommandBufferAllocator, |
| synced::{Command, Resource, SyncCommandBufferBuilder, SyncCommandBufferBuilderError}, |
| sys::UnsafeCommandBufferBuilder, |
| AutoCommandBufferBuilder, ResourceInCommand, ResourceUseRef, |
| }, |
| device::{DeviceOwned, QueueFlags}, |
| format::{Format, FormatFeatures, NumericType}, |
| image::{ |
| ImageAccess, ImageAspects, ImageDimensions, ImageLayout, ImageSubresourceLayers, ImageType, |
| ImageUsage, SampleCount, SampleCounts, |
| }, |
| sampler::Filter, |
| sync::{AccessFlags, PipelineMemoryAccess, PipelineStages}, |
| DeviceSize, RequirementNotMet, RequiresOneOf, Version, VulkanObject, |
| }; |
| use smallvec::{smallvec, SmallVec}; |
| use std::{ |
| cmp::{max, min}, |
| error::Error, |
| fmt::{Display, Error as FmtError, Formatter}, |
| mem::size_of, |
| sync::Arc, |
| }; |
| |
| /// # Commands to transfer data between resources. |
| impl<L, A> AutoCommandBufferBuilder<L, A> |
| where |
| A: CommandBufferAllocator, |
| { |
| /// Copies data from a buffer to another buffer. |
| /// |
| /// # Panics |
| /// |
| /// - Panics if `src_buffer` or `dst_buffer` were not created from the same device |
| /// as `self`. |
| pub fn copy_buffer( |
| &mut self, |
| copy_buffer_info: impl Into<CopyBufferInfo>, |
| ) -> Result<&mut Self, CopyError> { |
| let copy_buffer_info = copy_buffer_info.into(); |
| self.validate_copy_buffer(©_buffer_info)?; |
| |
| unsafe { |
| self.inner.copy_buffer(copy_buffer_info)?; |
| } |
| |
| Ok(self) |
| } |
| |
| fn validate_copy_buffer(&self, copy_buffer_info: &CopyBufferInfo) -> Result<(), CopyError> { |
| let device = self.device(); |
| |
| // VUID-vkCmdCopyBuffer2-renderpass |
| if self.render_pass_state.is_some() { |
| return Err(CopyError::ForbiddenInsideRenderPass); |
| } |
| |
| let queue_family_properties = self.queue_family_properties(); |
| |
| // VUID-vkCmdCopyBuffer2-commandBuffer-cmdpool |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) |
| { |
| return Err(CopyError::NotSupportedByQueueFamily); |
| } |
| |
| let &CopyBufferInfo { |
| ref src_buffer, |
| ref dst_buffer, |
| ref regions, |
| _ne: _, |
| } = copy_buffer_info; |
| |
| // VUID-VkCopyBufferInfo2-commonparent |
| assert_eq!(device, src_buffer.device()); |
| assert_eq!(device, dst_buffer.device()); |
| |
| // VUID-VkCopyBufferInfo2-srcBuffer-00118 |
| if !src_buffer |
| .buffer() |
| .usage() |
| .intersects(BufferUsage::TRANSFER_SRC) |
| { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Source, |
| usage: "transfer_src", |
| }); |
| } |
| |
| // VUID-VkCopyBufferInfo2-dstBuffer-00120 |
| if !dst_buffer |
| .buffer() |
| .usage() |
| .intersects(BufferUsage::TRANSFER_DST) |
| { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Destination, |
| usage: "transfer_dst", |
| }); |
| } |
| |
| let same_buffer = src_buffer.buffer() == dst_buffer.buffer(); |
| let mut overlap_indices = None; |
| |
| for (region_index, region) in regions.iter().enumerate() { |
| let &BufferCopy { |
| src_offset, |
| dst_offset, |
| size, |
| _ne: _, |
| } = region; |
| |
| // VUID-VkBufferCopy2-size-01988 |
| assert!(size != 0); |
| |
| // VUID-VkCopyBufferInfo2-srcOffset-00113 |
| // VUID-VkCopyBufferInfo2-size-00115 |
| if src_offset + size > src_buffer.size() { |
| return Err(CopyError::RegionOutOfBufferBounds { |
| resource: CopyErrorResource::Source, |
| region_index, |
| offset_range_end: src_offset + size, |
| buffer_size: src_buffer.size(), |
| }); |
| } |
| |
| // VUID-VkCopyBufferInfo2-dstOffset-00114 |
| // VUID-VkCopyBufferInfo2-size-00116 |
| if dst_offset + size > dst_buffer.size() { |
| return Err(CopyError::RegionOutOfBufferBounds { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| offset_range_end: dst_offset + size, |
| buffer_size: dst_buffer.size(), |
| }); |
| } |
| |
| // VUID-VkCopyBufferInfo2-pRegions-00117 |
| if same_buffer { |
| let src_region_index = region_index; |
| let src_range = |
| src_buffer.offset() + src_offset..src_buffer.offset() + src_offset + size; |
| |
| for (dst_region_index, dst_region) in regions.iter().enumerate() { |
| let &BufferCopy { dst_offset, .. } = dst_region; |
| |
| let dst_range = |
| dst_buffer.offset() + dst_offset..dst_buffer.offset() + dst_offset + size; |
| |
| if src_range.start >= dst_range.end || dst_range.start >= src_range.end { |
| // The regions do not overlap |
| continue; |
| } |
| |
| overlap_indices = Some((src_region_index, dst_region_index)); |
| } |
| } |
| } |
| |
| // VUID-VkCopyBufferInfo2-pRegions-00117 |
| if let Some((src_region_index, dst_region_index)) = overlap_indices { |
| return Err(CopyError::OverlappingRegions { |
| src_region_index, |
| dst_region_index, |
| }); |
| } |
| |
| Ok(()) |
| } |
| |
| /// Copies data from an image to another image. |
| /// |
| /// There are several restrictions: |
| /// |
| /// - The number of samples in the source and destination images must be equal. |
| /// - The size of the uncompressed element format of the source image must be equal to the |
| /// compressed element format of the destination. |
| /// - If you copy between depth, stencil or depth-stencil images, the format of both images |
| /// must match exactly. |
| /// - For two-dimensional images, the Z coordinate must be 0 for the image offsets and 1 for |
| /// the extent. Same for the Y coordinate for one-dimensional images. |
| /// - For non-array images, the base array layer must be 0 and the number of layers must be 1. |
| /// |
| /// If `layer_count` is greater than 1, the copy will happen between each individual layer as |
| /// if they were separate images. |
| /// |
| /// # Panics |
| /// |
| /// - Panics if `src_image` or `dst_image` were not created from the same device |
| /// as `self`. |
| pub fn copy_image(&mut self, copy_image_info: CopyImageInfo) -> Result<&mut Self, CopyError> { |
| self.validate_copy_image(©_image_info)?; |
| |
| unsafe { |
| self.inner.copy_image(copy_image_info)?; |
| } |
| |
| Ok(self) |
| } |
| |
| fn validate_copy_image(&self, copy_image_info: &CopyImageInfo) -> Result<(), CopyError> { |
| let device = self.device(); |
| |
| // VUID-vkCmdCopyImage2-renderpass |
| if self.render_pass_state.is_some() { |
| return Err(CopyError::ForbiddenInsideRenderPass); |
| } |
| |
| let queue_family_properties = self.queue_family_properties(); |
| |
| // VUID-vkCmdCopyImage2-commandBuffer-cmdpool |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) |
| { |
| return Err(CopyError::NotSupportedByQueueFamily); |
| } |
| |
| let &CopyImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = copy_image_info; |
| |
| // VUID-VkCopyImageInfo2-srcImageLayout-parameter |
| src_image_layout.validate_device(device)?; |
| |
| // VUID-VkCopyImageInfo2-dstImageLayout-parameter |
| dst_image_layout.validate_device(device)?; |
| |
| // VUID-VkCopyImageInfo2-commonparent |
| assert_eq!(device, src_image.device()); |
| assert_eq!(device, dst_image.device()); |
| |
| let copy_2d_3d_supported = |
| device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1; |
| let src_image_inner = src_image.inner(); |
| let dst_image_inner = dst_image.inner(); |
| let mut src_image_aspects = src_image.format().aspects(); |
| let mut dst_image_aspects = dst_image.format().aspects(); |
| |
| if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { |
| // VUID-VkCopyImageInfo2-srcImage-01995 |
| if !src_image |
| .format_features() |
| .intersects(FormatFeatures::TRANSFER_SRC) |
| { |
| return Err(CopyError::MissingFormatFeature { |
| resource: CopyErrorResource::Source, |
| format_feature: "transfer_src", |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-dstImage-01996 |
| if !dst_image |
| .format_features() |
| .intersects(FormatFeatures::TRANSFER_DST) |
| { |
| return Err(CopyError::MissingFormatFeature { |
| resource: CopyErrorResource::Destination, |
| format_feature: "transfer_dst", |
| }); |
| } |
| } |
| |
| // VUID-VkCopyImageInfo2-srcImage-00136 |
| if src_image.samples() != dst_image.samples() { |
| return Err(CopyError::SampleCountMismatch { |
| src_sample_count: src_image.samples(), |
| dst_sample_count: dst_image.samples(), |
| }); |
| } |
| |
| if !(src_image_aspects.intersects(ImageAspects::COLOR) |
| || dst_image_aspects.intersects(ImageAspects::COLOR)) |
| { |
| // VUID-VkCopyImageInfo2-srcImage-01548 |
| if src_image.format() != dst_image.format() { |
| return Err(CopyError::FormatsMismatch { |
| src_format: src_image.format(), |
| dst_format: dst_image.format(), |
| }); |
| } |
| } |
| |
| // VUID-VkCopyImageInfo2-srcImageLayout-01917 |
| if !matches!( |
| src_image_layout, |
| ImageLayout::TransferSrcOptimal | ImageLayout::General |
| ) { |
| return Err(CopyError::ImageLayoutInvalid { |
| resource: CopyErrorResource::Source, |
| image_layout: src_image_layout, |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-dstImageLayout-01395 |
| if !matches!( |
| dst_image_layout, |
| ImageLayout::TransferDstOptimal | ImageLayout::General |
| ) { |
| return Err(CopyError::ImageLayoutInvalid { |
| resource: CopyErrorResource::Destination, |
| image_layout: dst_image_layout, |
| }); |
| } |
| |
| let extent_alignment = match queue_family_properties.min_image_transfer_granularity { |
| [0, 0, 0] => None, |
| min_image_transfer_granularity => { |
| let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| { |
| if is_multi_plane { |
| // Assume planes always have 1x1 blocks |
| min_image_transfer_granularity |
| } else { |
| // "The value returned in minImageTransferGranularity has a unit of |
| // compressed texel blocks for images having a block-compressed format, and |
| // a unit of texels otherwise." |
| [ |
| min_image_transfer_granularity[0] * block_extent[0], |
| min_image_transfer_granularity[1] * block_extent[1], |
| min_image_transfer_granularity[2] * block_extent[2], |
| ] |
| } |
| }; |
| |
| Some(( |
| granularity( |
| src_image.format().block_extent(), |
| src_image_aspects.intersects(ImageAspects::PLANE_0), |
| ), |
| granularity( |
| dst_image.format().block_extent(), |
| dst_image_aspects.intersects(ImageAspects::PLANE_0), |
| ), |
| )) |
| } |
| }; |
| |
| if src_image_aspects.intersects(ImageAspects::PLANE_0) { |
| // VUID-VkCopyImageInfo2-srcImage-01552 |
| // VUID-VkCopyImageInfo2-srcImage-01553 |
| src_image_aspects -= ImageAspects::COLOR; |
| } |
| |
| if dst_image_aspects.intersects(ImageAspects::PLANE_0) { |
| // VUID-VkCopyImageInfo2-dstImage-01554 |
| // VUID-VkCopyImageInfo2-dstImage-01555 |
| dst_image_aspects -= ImageAspects::COLOR; |
| } |
| |
| let mut src_image_aspects_used = ImageAspects::empty(); |
| let mut dst_image_aspects_used = ImageAspects::empty(); |
| let same_image = src_image_inner.image == dst_image_inner.image; |
| let mut overlap_subresource_indices = None; |
| let mut overlap_extent_indices = None; |
| |
| for (region_index, region) in regions.iter().enumerate() { |
| let &ImageCopy { |
| ref src_subresource, |
| src_offset, |
| ref dst_subresource, |
| dst_offset, |
| extent, |
| _ne, |
| } = region; |
| |
| let check_subresource = |resource: CopyErrorResource, |
| image: &dyn ImageAccess, |
| image_aspects: ImageAspects, |
| subresource: &ImageSubresourceLayers| |
| -> Result<_, CopyError> { |
| // VUID-VkCopyImageInfo2-srcSubresource-01696 |
| // VUID-VkCopyImageInfo2-dstSubresource-01697 |
| if subresource.mip_level >= image.mip_levels() { |
| return Err(CopyError::MipLevelsOutOfRange { |
| resource, |
| region_index, |
| mip_levels_range_end: subresource.mip_level + 1, |
| image_mip_levels: image.mip_levels(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-layerCount-01700 |
| assert!(!subresource.array_layers.is_empty()); |
| |
| // VUID-VkCopyImageInfo2-srcSubresource-01698 |
| // VUID-VkCopyImageInfo2-dstSubresource-01699 |
| // VUID-VkCopyImageInfo2-srcImage-04443 |
| // VUID-VkCopyImageInfo2-dstImage-04444 |
| if subresource.array_layers.end > image.dimensions().array_layers() { |
| return Err(CopyError::ArrayLayersOutOfRange { |
| resource, |
| region_index, |
| array_layers_range_end: subresource.array_layers.end, |
| image_array_layers: image.dimensions().array_layers(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-aspectMask-parameter |
| subresource.aspects.validate_device(device)?; |
| |
| // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask |
| assert!(!subresource.aspects.is_empty()); |
| |
| // VUID-VkCopyImageInfo2-aspectMask-00142 |
| // VUID-VkCopyImageInfo2-aspectMask-00143 |
| if !image_aspects.contains(subresource.aspects) { |
| return Err(CopyError::AspectsNotAllowed { |
| resource, |
| region_index, |
| aspects: subresource.aspects, |
| allowed_aspects: image_aspects, |
| }); |
| } |
| |
| let (subresource_format, subresource_extent) = |
| if image_aspects.intersects(ImageAspects::PLANE_0) { |
| // VUID-VkCopyImageInfo2-srcImage-01552 |
| // VUID-VkCopyImageInfo2-srcImage-01553 |
| // VUID-VkCopyImageInfo2-dstImage-01554 |
| // VUID-VkCopyImageInfo2-dstImage-01555 |
| if subresource.aspects.count() != 1 { |
| return Err(CopyError::MultipleAspectsNotAllowed { |
| resource, |
| region_index, |
| aspects: subresource.aspects, |
| }); |
| } |
| |
| if subresource.aspects.intersects(ImageAspects::PLANE_0) { |
| ( |
| image.format().planes()[0], |
| image.dimensions().width_height_depth(), |
| ) |
| } else if subresource.aspects.intersects(ImageAspects::PLANE_1) { |
| ( |
| image.format().planes()[1], |
| image |
| .format() |
| .ycbcr_chroma_sampling() |
| .unwrap() |
| .subsampled_extent(image.dimensions().width_height_depth()), |
| ) |
| } else { |
| ( |
| image.format().planes()[2], |
| image |
| .format() |
| .ycbcr_chroma_sampling() |
| .unwrap() |
| .subsampled_extent(image.dimensions().width_height_depth()), |
| ) |
| } |
| } else { |
| ( |
| image.format(), |
| image |
| .dimensions() |
| .mip_level_dimensions(subresource.mip_level) |
| .unwrap() |
| .width_height_depth(), |
| ) |
| }; |
| |
| Ok((subresource_format, subresource_extent)) |
| }; |
| |
| src_image_aspects_used |= src_subresource.aspects; |
| dst_image_aspects_used |= dst_subresource.aspects; |
| |
| let (src_subresource_format, src_subresource_extent) = check_subresource( |
| CopyErrorResource::Source, |
| src_image, |
| src_image_aspects, |
| src_subresource, |
| )?; |
| let (dst_subresource_format, dst_subresource_extent) = check_subresource( |
| CopyErrorResource::Destination, |
| dst_image, |
| dst_image_aspects, |
| dst_subresource, |
| )?; |
| |
| if !(src_image_aspects.intersects(ImageAspects::PLANE_0) |
| || dst_image_aspects.intersects(ImageAspects::PLANE_0)) |
| { |
| // VUID-VkCopyImageInfo2-srcImage-01551 |
| if src_subresource.aspects != dst_subresource.aspects { |
| return Err(CopyError::AspectsMismatch { |
| region_index, |
| src_aspects: src_subresource.aspects, |
| dst_aspects: dst_subresource.aspects, |
| }); |
| } |
| } |
| |
| // VUID-VkCopyImageInfo2-srcImage-01548 |
| // VUID-VkCopyImageInfo2-None-01549 |
| // Color formats must be size-compatible. |
| if src_subresource_format.block_size() != dst_subresource_format.block_size() { |
| return Err(CopyError::FormatsNotCompatible { |
| src_format: src_subresource_format, |
| dst_format: dst_subresource_format, |
| }); |
| } |
| |
| // TODO: |
| // "When copying between compressed and uncompressed formats the extent members |
| // represent the texel dimensions of the source image and not the destination." |
| let mut src_extent = extent; |
| let mut dst_extent = extent; |
| let src_layer_count = |
| src_subresource.array_layers.end - src_subresource.array_layers.start; |
| let dst_layer_count = |
| dst_subresource.array_layers.end - dst_subresource.array_layers.start; |
| |
| if copy_2d_3d_supported { |
| match ( |
| src_image.dimensions().image_type(), |
| dst_image.dimensions().image_type(), |
| ) { |
| (ImageType::Dim2d, ImageType::Dim3d) => { |
| src_extent[2] = 1; |
| |
| // VUID-vkCmdCopyImage-srcImage-01791 |
| if dst_extent[2] != src_layer_count { |
| return Err(CopyError::ArrayLayerCountMismatch { |
| region_index, |
| src_layer_count, |
| dst_layer_count: dst_extent[2], |
| }); |
| } |
| } |
| (ImageType::Dim3d, ImageType::Dim2d) => { |
| dst_extent[2] = 1; |
| |
| // VUID-vkCmdCopyImage-dstImage-01792 |
| if src_extent[2] != dst_layer_count { |
| return Err(CopyError::ArrayLayerCountMismatch { |
| region_index, |
| src_layer_count: src_extent[2], |
| dst_layer_count, |
| }); |
| } |
| } |
| _ => { |
| // VUID-VkImageCopy2-extent-00140 |
| if src_layer_count != dst_layer_count { |
| return Err(CopyError::ArrayLayerCountMismatch { |
| region_index, |
| src_layer_count, |
| dst_layer_count, |
| }); |
| } |
| } |
| } |
| } else { |
| // VUID-VkImageCopy2-extent-00140 |
| if src_layer_count != dst_layer_count { |
| return Err(CopyError::ArrayLayerCountMismatch { |
| region_index, |
| src_layer_count, |
| dst_layer_count, |
| }); |
| } |
| }; |
| |
| if let Some((src_extent_alignment, dst_extent_alignment)) = extent_alignment { |
| let check_offset_extent = |resource: CopyErrorResource, |
| extent_alignment: [u32; 3], |
| subresource_extent: [u32; 3], |
| offset: [u32; 3], |
| extent: [u32; 3]| |
| -> Result<_, CopyError> { |
| for i in 0..3 { |
| // VUID-VkImageCopy2-extent-06668 |
| // VUID-VkImageCopy2-extent-06669 |
| // VUID-VkImageCopy2-extent-06670 |
| assert!(extent[i] != 0); |
| |
| // VUID-VkCopyImageInfo2-srcOffset-00144 |
| // VUID-VkCopyImageInfo2-srcOffset-00145 |
| // VUID-VkCopyImageInfo2-srcOffset-00147 |
| // VUID-VkCopyImageInfo2-dstOffset-00150 |
| // VUID-VkCopyImageInfo2-dstOffset-00151 |
| // VUID-VkCopyImageInfo2-dstOffset-00153 |
| if offset[i] + extent[i] > subresource_extent[i] { |
| return Err(CopyError::RegionOutOfImageBounds { |
| resource, |
| region_index, |
| offset_range_end: [ |
| offset[0] + extent[0], |
| offset[1] + extent[1], |
| offset[2] + extent[2], |
| ], |
| subresource_extent, |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-srcImage-01727 |
| // VUID-VkCopyImageInfo2-dstImage-01731 |
| // VUID-VkCopyImageInfo2-srcOffset-01783 |
| // VUID-VkCopyImageInfo2-dstOffset-01784 |
| if offset[i] % extent_alignment[i] != 0 { |
| return Err(CopyError::OffsetNotAlignedForImage { |
| resource, |
| region_index, |
| offset, |
| required_alignment: extent_alignment, |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-srcImage-01728 |
| // VUID-VkCopyImageInfo2-srcImage-01729 |
| // VUID-VkCopyImageInfo2-srcImage-01730 |
| // VUID-VkCopyImageInfo2-dstImage-01732 |
| // VUID-VkCopyImageInfo2-dstImage-01733 |
| // VUID-VkCopyImageInfo2-dstImage-01734 |
| if offset[i] + extent[i] != subresource_extent[i] |
| && extent[i] % extent_alignment[i] != 0 |
| { |
| return Err(CopyError::ExtentNotAlignedForImage { |
| resource, |
| region_index, |
| extent, |
| required_alignment: extent_alignment, |
| }); |
| } |
| } |
| |
| Ok(()) |
| }; |
| |
| check_offset_extent( |
| CopyErrorResource::Source, |
| src_extent_alignment, |
| src_subresource_extent, |
| src_offset, |
| src_extent, |
| )?; |
| check_offset_extent( |
| CopyErrorResource::Destination, |
| dst_extent_alignment, |
| dst_subresource_extent, |
| dst_offset, |
| dst_extent, |
| )?; |
| |
| // VUID-VkCopyImageInfo2-pRegions-00124 |
| if same_image { |
| let src_region_index = region_index; |
| let src_subresource_axes = [ |
| src_image_inner.first_mipmap_level + src_subresource.mip_level |
| ..src_image_inner.first_mipmap_level + src_subresource.mip_level + 1, |
| src_image_inner.first_layer + src_subresource.array_layers.start |
| ..src_image_inner.first_layer + src_subresource.array_layers.end, |
| ]; |
| let src_extent_axes = [ |
| src_offset[0]..src_offset[0] + extent[0], |
| src_offset[1]..src_offset[1] + extent[1], |
| src_offset[2]..src_offset[2] + extent[2], |
| ]; |
| |
| for (dst_region_index, dst_region) in regions.iter().enumerate() { |
| let &ImageCopy { |
| ref dst_subresource, |
| dst_offset, |
| .. |
| } = dst_region; |
| |
| // For a single-plane image, the aspects must always be identical anyway |
| if src_image_aspects.intersects(ImageAspects::PLANE_0) |
| && src_subresource.aspects != dst_subresource.aspects |
| { |
| continue; |
| } |
| |
| let dst_subresource_axes = [ |
| dst_image_inner.first_mipmap_level + dst_subresource.mip_level |
| ..dst_image_inner.first_mipmap_level |
| + dst_subresource.mip_level |
| + 1, |
| dst_image_inner.first_layer + src_subresource.array_layers.start |
| ..dst_image_inner.first_layer + src_subresource.array_layers.end, |
| ]; |
| |
| if src_subresource_axes.iter().zip(dst_subresource_axes).any( |
| |(src_range, dst_range)| { |
| src_range.start >= dst_range.end || dst_range.start >= src_range.end |
| }, |
| ) { |
| continue; |
| } |
| |
| // If the subresource axes all overlap, then the source and destination must |
| // have the same layout. |
| overlap_subresource_indices = Some((src_region_index, dst_region_index)); |
| |
| let dst_extent_axes = [ |
| dst_offset[0]..dst_offset[0] + extent[0], |
| dst_offset[1]..dst_offset[1] + extent[1], |
| dst_offset[2]..dst_offset[2] + extent[2], |
| ]; |
| |
| // There is only overlap if all of the axes overlap. |
| if src_extent_axes.iter().zip(dst_extent_axes).any( |
| |(src_range, dst_range)| { |
| src_range.start >= dst_range.end || dst_range.start >= src_range.end |
| }, |
| ) { |
| continue; |
| } |
| |
| overlap_extent_indices = Some((src_region_index, dst_region_index)); |
| } |
| } |
| } else { |
| // If granularity is `None`, then we can only copy whole subresources. |
| let check_offset_extent = |resource: CopyErrorResource, |
| subresource_extent: [u32; 3], |
| offset: [u32; 3], |
| extent: [u32; 3]| |
| -> Result<_, CopyError> { |
| // VUID-VkCopyImageInfo2-srcImage-01727 |
| // VUID-VkCopyImageInfo2-dstImage-01731 |
| // VUID-vkCmdCopyImage-srcOffset-01783 |
| // VUID-vkCmdCopyImage-dstOffset-01784 |
| if offset != [0, 0, 0] { |
| return Err(CopyError::OffsetNotAlignedForImage { |
| resource, |
| region_index, |
| offset, |
| required_alignment: subresource_extent, |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-srcImage-01728 |
| // VUID-VkCopyImageInfo2-srcImage-01729 |
| // VUID-VkCopyImageInfo2-srcImage-01730 |
| // VUID-VkCopyImageInfo2-dstImage-01732 |
| // VUID-VkCopyImageInfo2-dstImage-01733 |
| // VUID-VkCopyImageInfo2-dstImage-01734 |
| if extent != subresource_extent { |
| return Err(CopyError::ExtentNotAlignedForImage { |
| resource, |
| region_index, |
| extent, |
| required_alignment: subresource_extent, |
| }); |
| } |
| |
| Ok(()) |
| }; |
| |
| check_offset_extent( |
| CopyErrorResource::Source, |
| src_subresource_extent, |
| src_offset, |
| src_extent, |
| )?; |
| check_offset_extent( |
| CopyErrorResource::Destination, |
| dst_subresource_extent, |
| dst_offset, |
| dst_extent, |
| )?; |
| |
| // VUID-VkCopyImageInfo2-pRegions-00124 |
| // A simpler version that assumes the region covers the full extent. |
| if same_image { |
| let src_region_index = region_index; |
| let src_axes = [ |
| src_image_inner.first_mipmap_level + src_subresource.mip_level |
| ..src_image_inner.first_mipmap_level + src_subresource.mip_level + 1, |
| src_image_inner.first_layer + src_subresource.array_layers.start |
| ..src_image_inner.first_layer + src_subresource.array_layers.end, |
| ]; |
| |
| for (dst_region_index, dst_region) in regions.iter().enumerate() { |
| let &ImageCopy { |
| ref dst_subresource, |
| dst_offset: _, |
| .. |
| } = dst_region; |
| |
| if src_image_aspects.intersects(ImageAspects::PLANE_0) |
| && src_subresource.aspects != dst_subresource.aspects |
| { |
| continue; |
| } |
| |
| let dst_axes = [ |
| dst_image_inner.first_mipmap_level + dst_subresource.mip_level |
| ..dst_image_inner.first_mipmap_level |
| + dst_subresource.mip_level |
| + 1, |
| dst_image_inner.first_layer + src_subresource.array_layers.start |
| ..dst_image_inner.first_layer + src_subresource.array_layers.end, |
| ]; |
| |
| // There is only overlap if all of the axes overlap. |
| if src_axes.iter().zip(dst_axes).any(|(src_range, dst_range)| { |
| src_range.start >= dst_range.end || dst_range.start >= src_range.end |
| }) { |
| continue; |
| } |
| |
| overlap_extent_indices = Some((src_region_index, dst_region_index)); |
| } |
| } |
| } |
| } |
| |
| // VUID-VkCopyImageInfo2-aspect-06662 |
| if !(src_image_aspects_used - ImageAspects::STENCIL).is_empty() |
| && !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) |
| { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Source, |
| usage: "transfer_src", |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-aspect-06663 |
| if !(dst_image_aspects_used - ImageAspects::STENCIL).is_empty() |
| && !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) |
| { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Destination, |
| usage: "transfer_dst", |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-aspect-06664 |
| if src_image_aspects_used.intersects(ImageAspects::STENCIL) |
| && !src_image |
| .stencil_usage() |
| .intersects(ImageUsage::TRANSFER_SRC) |
| { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Source, |
| usage: "transfer_src", |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-aspect-06665 |
| if dst_image_aspects_used.intersects(ImageAspects::STENCIL) |
| && !dst_image |
| .stencil_usage() |
| .intersects(ImageUsage::TRANSFER_DST) |
| { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Destination, |
| usage: "transfer_dst", |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-pRegions-00124 |
| if let Some((src_region_index, dst_region_index)) = overlap_extent_indices { |
| return Err(CopyError::OverlappingRegions { |
| src_region_index, |
| dst_region_index, |
| }); |
| } |
| |
| // VUID-VkCopyImageInfo2-srcImageLayout-00128 |
| // VUID-VkCopyImageInfo2-dstImageLayout-00133 |
| if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices { |
| if src_image_layout != dst_image_layout { |
| return Err(CopyError::OverlappingSubresourcesLayoutMismatch { |
| src_region_index, |
| dst_region_index, |
| src_image_layout, |
| dst_image_layout, |
| }); |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| /// Copies from a buffer to an image. |
| pub fn copy_buffer_to_image( |
| &mut self, |
| copy_buffer_to_image_info: CopyBufferToImageInfo, |
| ) -> Result<&mut Self, CopyError> { |
| self.validate_copy_buffer_to_image(©_buffer_to_image_info)?; |
| |
| unsafe { |
| self.inner.copy_buffer_to_image(copy_buffer_to_image_info)?; |
| } |
| |
| Ok(self) |
| } |
| |
| fn validate_copy_buffer_to_image( |
| &self, |
| copy_buffer_to_image_info: &CopyBufferToImageInfo, |
| ) -> Result<(), CopyError> { |
| let device = self.device(); |
| |
| // VUID-vkCmdCopyBufferToImage2-renderpass |
| if self.render_pass_state.is_some() { |
| return Err(CopyError::ForbiddenInsideRenderPass); |
| } |
| |
| let queue_family_properties = self.queue_family_properties(); |
| |
| // VUID-vkCmdCopyBufferToImage2-commandBuffer-cmdpool |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) |
| { |
| return Err(CopyError::NotSupportedByQueueFamily); |
| } |
| |
| let &CopyBufferToImageInfo { |
| ref src_buffer, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = copy_buffer_to_image_info; |
| |
| // VUID-VkCopyBufferToImageInfo2-dstImageLayout-parameter |
| dst_image_layout.validate_device(device)?; |
| |
| // VUID-VkCopyBufferToImageInfo2-commonparent |
| assert_eq!(device, src_buffer.device()); |
| assert_eq!(device, dst_image.device()); |
| |
| let mut image_aspects = dst_image.format().aspects(); |
| |
| // VUID-VkCopyBufferToImageInfo2-commandBuffer-04477 |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::GRAPHICS) |
| && !image_aspects.intersects(ImageAspects::COLOR) |
| { |
| return Err(CopyError::DepthStencilNotSupportedByQueueFamily); |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-srcBuffer-00174 |
| if !src_buffer |
| .buffer() |
| .usage() |
| .intersects(BufferUsage::TRANSFER_SRC) |
| { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Source, |
| usage: "transfer_src", |
| }); |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-dstImage-00177 |
| if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Destination, |
| usage: "transfer_dst", |
| }); |
| } |
| |
| if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { |
| // VUID-VkCopyBufferToImageInfo2-dstImage-01997 |
| if !dst_image |
| .format_features() |
| .intersects(FormatFeatures::TRANSFER_DST) |
| { |
| return Err(CopyError::MissingFormatFeature { |
| resource: CopyErrorResource::Destination, |
| format_feature: "transfer_dst", |
| }); |
| } |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-dstImage-00179 |
| if dst_image.samples() != SampleCount::Sample1 { |
| return Err(CopyError::SampleCountInvalid { |
| resource: CopyErrorResource::Destination, |
| sample_count: dst_image.samples(), |
| allowed_sample_counts: SampleCounts::SAMPLE_1, |
| }); |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-dstImageLayout-01396 |
| if !matches!( |
| dst_image_layout, |
| ImageLayout::TransferDstOptimal | ImageLayout::General |
| ) { |
| return Err(CopyError::ImageLayoutInvalid { |
| resource: CopyErrorResource::Destination, |
| image_layout: dst_image_layout, |
| }); |
| } |
| |
| let extent_alignment = match queue_family_properties.min_image_transfer_granularity { |
| [0, 0, 0] => None, |
| min_image_transfer_granularity => { |
| let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| { |
| if is_multi_plane { |
| // Assume planes always have 1x1 blocks |
| min_image_transfer_granularity |
| } else { |
| // "The value returned in minImageTransferGranularity has a unit of |
| // compressed texel blocks for images having a block-compressed format, and |
| // a unit of texels otherwise." |
| [ |
| min_image_transfer_granularity[0] * block_extent[0], |
| min_image_transfer_granularity[1] * block_extent[1], |
| min_image_transfer_granularity[2] * block_extent[2], |
| ] |
| } |
| }; |
| |
| Some(granularity( |
| dst_image.format().block_extent(), |
| image_aspects.intersects(ImageAspects::PLANE_0), |
| )) |
| } |
| }; |
| |
| if image_aspects.intersects(ImageAspects::PLANE_0) { |
| // VUID-VkCopyBufferToImageInfo2-aspectMask-01560 |
| image_aspects -= ImageAspects::COLOR; |
| } |
| |
| for (region_index, region) in regions.iter().enumerate() { |
| let &BufferImageCopy { |
| buffer_offset, |
| buffer_row_length, |
| buffer_image_height, |
| ref image_subresource, |
| image_offset, |
| image_extent, |
| _ne: _, |
| } = region; |
| |
| // VUID-VkCopyBufferToImageInfo2-imageSubresource-01701 |
| if image_subresource.mip_level >= dst_image.mip_levels() { |
| return Err(CopyError::MipLevelsOutOfRange { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| mip_levels_range_end: image_subresource.mip_level + 1, |
| image_mip_levels: dst_image.mip_levels(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-layerCount-01700 |
| // VUID-VkCopyBufferToImageInfo2-baseArrayLayer-00213 |
| assert!(!image_subresource.array_layers.is_empty()); |
| |
| // VUID-VkCopyBufferToImageInfo2-imageSubresource-01702 |
| // VUID-VkCopyBufferToImageInfo2-baseArrayLayer-00213 |
| if image_subresource.array_layers.end > dst_image.dimensions().array_layers() { |
| return Err(CopyError::ArrayLayersOutOfRange { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| array_layers_range_end: image_subresource.array_layers.end, |
| image_array_layers: dst_image.dimensions().array_layers(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask |
| assert!(!image_subresource.aspects.is_empty()); |
| |
| // VUID-VkCopyBufferToImageInfo2-aspectMask-00211 |
| if !image_aspects.contains(image_subresource.aspects) { |
| return Err(CopyError::AspectsNotAllowed { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| aspects: image_subresource.aspects, |
| allowed_aspects: image_aspects, |
| }); |
| } |
| |
| // VUID-VkBufferImageCopy2-aspectMask-00212 |
| // VUID-VkCopyBufferToImageInfo2-aspectMask-01560 |
| if image_subresource.aspects.count() != 1 { |
| return Err(CopyError::MultipleAspectsNotAllowed { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| aspects: image_subresource.aspects, |
| }); |
| } |
| |
| let (image_subresource_format, image_subresource_extent) = |
| if image_aspects.intersects(ImageAspects::PLANE_0) { |
| if image_subresource.aspects.intersects(ImageAspects::PLANE_0) { |
| ( |
| dst_image.format().planes()[0], |
| dst_image.dimensions().width_height_depth(), |
| ) |
| } else if image_subresource.aspects.intersects(ImageAspects::PLANE_1) { |
| ( |
| dst_image.format().planes()[1], |
| dst_image |
| .format() |
| .ycbcr_chroma_sampling() |
| .unwrap() |
| .subsampled_extent(dst_image.dimensions().width_height_depth()), |
| ) |
| } else { |
| ( |
| dst_image.format().planes()[2], |
| dst_image |
| .format() |
| .ycbcr_chroma_sampling() |
| .unwrap() |
| .subsampled_extent(dst_image.dimensions().width_height_depth()), |
| ) |
| } |
| } else { |
| ( |
| dst_image.format(), |
| dst_image |
| .dimensions() |
| .mip_level_dimensions(image_subresource.mip_level) |
| .unwrap() |
| .width_height_depth(), |
| ) |
| }; |
| |
| if let Some(extent_alignment) = extent_alignment { |
| for i in 0..3 { |
| // VUID-VkBufferImageCopy2-imageExtent-06659 |
| // VUID-VkBufferImageCopy2-imageExtent-06660 |
| // VUID-VkBufferImageCopy2-imageExtent-06661 |
| assert!(image_extent[i] != 0); |
| |
| // VUID-VkCopyBufferToImageInfo2-pRegions-06223 |
| // VUID-VkCopyBufferToImageInfo2-pRegions-06224 |
| // VUID-VkCopyBufferToImageInfo2-imageOffset-00200 |
| if image_offset[i] + image_extent[i] > image_subresource_extent[i] { |
| return Err(CopyError::RegionOutOfImageBounds { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| offset_range_end: [ |
| image_offset[0] + image_extent[0], |
| image_offset[1] + image_extent[1], |
| image_offset[2] + image_extent[2], |
| ], |
| subresource_extent: image_subresource_extent, |
| }); |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 |
| // VUID-VkCopyBufferToImageInfo2-imageOffset-00205 |
| if image_offset[i] % extent_alignment[i] != 0 { |
| return Err(CopyError::OffsetNotAlignedForImage { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| offset: image_offset, |
| required_alignment: extent_alignment, |
| }); |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 |
| // VUID-VkCopyBufferToImageInfo2-imageExtent-00207 |
| // VUID-VkCopyBufferToImageInfo2-imageExtent-00208 |
| // VUID-VkCopyBufferToImageInfo2-imageExtent-00209 |
| if image_offset[i] + image_extent[i] != image_subresource_extent[i] |
| && image_extent[i] % extent_alignment[i] != 0 |
| { |
| return Err(CopyError::ExtentNotAlignedForImage { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| extent: image_extent, |
| required_alignment: extent_alignment, |
| }); |
| } |
| } |
| } else { |
| // If granularity is `None`, then we can only copy whole subresources. |
| |
| // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 |
| if image_offset != [0, 0, 0] { |
| return Err(CopyError::OffsetNotAlignedForImage { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| offset: image_offset, |
| required_alignment: image_subresource_extent, |
| }); |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 |
| if image_extent != image_subresource_extent { |
| return Err(CopyError::ExtentNotAlignedForImage { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| extent: image_extent, |
| required_alignment: image_subresource_extent, |
| }); |
| } |
| } |
| |
| // VUID-VkBufferImageCopy2-bufferRowLength-00195 |
| if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) { |
| return Err(CopyError::BufferRowLengthTooSmall { |
| resource: CopyErrorResource::Source, |
| region_index, |
| row_length: buffer_row_length, |
| min: image_extent[0], |
| }); |
| } |
| |
| // VUID-VkBufferImageCopy2-bufferImageHeight-00196 |
| if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) { |
| return Err(CopyError::BufferImageHeightTooSmall { |
| resource: CopyErrorResource::Source, |
| region_index, |
| image_height: buffer_image_height, |
| min: image_extent[1], |
| }); |
| } |
| |
| let image_subresource_block_extent = image_subresource_format.block_extent(); |
| |
| // VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203 |
| if buffer_row_length % image_subresource_block_extent[0] != 0 { |
| return Err(CopyError::BufferRowLengthNotAligned { |
| resource: CopyErrorResource::Source, |
| region_index, |
| row_length: buffer_row_length, |
| required_alignment: image_subresource_block_extent[0], |
| }); |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-bufferImageHeight-00204 |
| if buffer_image_height % image_subresource_block_extent[1] != 0 { |
| return Err(CopyError::BufferImageHeightNotAligned { |
| resource: CopyErrorResource::Source, |
| region_index, |
| image_height: buffer_image_height, |
| required_alignment: image_subresource_block_extent[1], |
| }); |
| } |
| |
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferImageCopy.html#_description |
| let image_subresource_block_size = |
| if image_subresource.aspects.intersects(ImageAspects::STENCIL) { |
| 1 |
| } else if image_subresource.aspects.intersects(ImageAspects::DEPTH) { |
| match image_subresource_format { |
| Format::D16_UNORM | Format::D16_UNORM_S8_UINT => 2, |
| Format::D32_SFLOAT |
| | Format::D32_SFLOAT_S8_UINT |
| | Format::X8_D24_UNORM_PACK32 |
| | Format::D24_UNORM_S8_UINT => 4, |
| _ => unreachable!(), |
| } |
| } else { |
| image_subresource_format.block_size().unwrap() |
| }; |
| |
| // VUID-VkCopyBufferToImageInfo2-pRegions-04725 |
| // VUID-VkCopyBufferToImageInfo2-pRegions-04726 |
| if (buffer_row_length / image_subresource_block_extent[0]) as DeviceSize |
| * image_subresource_block_size |
| > 0x7FFFFFFF |
| { |
| return Err(CopyError::BufferRowLengthTooLarge { |
| resource: CopyErrorResource::Source, |
| region_index, |
| buffer_row_length, |
| }); |
| } |
| |
| let buffer_offset_alignment = |
| if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { |
| 4 |
| } else { |
| let mut buffer_offset_alignment = image_subresource_block_size; |
| |
| // VUID-VkCopyBufferToImageInfo2-commandBuffer-04052 |
| // Make the alignment a multiple of 4. |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) |
| { |
| if buffer_offset_alignment % 2 != 0 { |
| buffer_offset_alignment *= 2; |
| } |
| |
| if buffer_offset_alignment % 4 != 0 { |
| buffer_offset_alignment *= 2; |
| } |
| } |
| |
| buffer_offset_alignment |
| }; |
| |
| // VUID-VkCopyBufferToImageInfo2-bufferOffset-00206 |
| // VUID-VkCopyBufferToImageInfo2-bufferOffset-01558 |
| // VUID-VkCopyBufferToImageInfo2-bufferOffset-01559 |
| // VUID-VkCopyBufferToImageInfo2-srcImage-04053 |
| if (src_buffer.offset() + buffer_offset) % buffer_offset_alignment != 0 { |
| return Err(CopyError::OffsetNotAlignedForBuffer { |
| resource: CopyErrorResource::Source, |
| region_index, |
| offset: src_buffer.offset() + buffer_offset, |
| required_alignment: buffer_offset_alignment, |
| }); |
| } |
| |
| let buffer_copy_size = region.buffer_copy_size(image_subresource_format); |
| |
| // VUID-VkCopyBufferToImageInfo2-pRegions-00171 |
| if buffer_offset + buffer_copy_size > src_buffer.size() { |
| return Err(CopyError::RegionOutOfBufferBounds { |
| resource: CopyErrorResource::Source, |
| region_index, |
| offset_range_end: buffer_offset + buffer_copy_size, |
| buffer_size: src_buffer.size(), |
| }); |
| } |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-pRegions-00173 |
| // Can't occur as long as memory aliasing isn't allowed. |
| |
| Ok(()) |
| } |
| |
| /// Copies from an image to a buffer. |
| pub fn copy_image_to_buffer( |
| &mut self, |
| copy_image_to_buffer_info: CopyImageToBufferInfo, |
| ) -> Result<&mut Self, CopyError> { |
| self.validate_copy_image_to_buffer(©_image_to_buffer_info)?; |
| |
| unsafe { |
| self.inner.copy_image_to_buffer(copy_image_to_buffer_info)?; |
| } |
| |
| Ok(self) |
| } |
| |
| fn validate_copy_image_to_buffer( |
| &self, |
| copy_image_to_buffer_info: &CopyImageToBufferInfo, |
| ) -> Result<(), CopyError> { |
| let device = self.device(); |
| |
| // VUID-vkCmdCopyImageToBuffer2-renderpass |
| if self.render_pass_state.is_some() { |
| return Err(CopyError::ForbiddenInsideRenderPass); |
| } |
| |
| let queue_family_properties = self.queue_family_properties(); |
| |
| // VUID-vkCmdCopyImageToBuffer2-commandBuffer-cmdpool |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE) |
| { |
| return Err(CopyError::NotSupportedByQueueFamily); |
| } |
| |
| let &CopyImageToBufferInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_buffer, |
| ref regions, |
| _ne: _, |
| } = copy_image_to_buffer_info; |
| |
| // VUID-VkCopyImageToBufferInfo2-srcImageLayout-parameter |
| src_image_layout.validate_device(device)?; |
| |
| // VUID-VkCopyImageToBufferInfo2-commonparent |
| assert_eq!(device, dst_buffer.device()); |
| assert_eq!(device, src_image.device()); |
| |
| let mut image_aspects = src_image.format().aspects(); |
| |
| // VUID-VkCopyImageToBufferInfo2-srcImage-00186 |
| if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Source, |
| usage: "transfer_src", |
| }); |
| } |
| |
| // VUID-VkCopyImageToBufferInfo2-dstBuffer-00191 |
| if !dst_buffer |
| .buffer() |
| .usage() |
| .intersects(BufferUsage::TRANSFER_DST) |
| { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Destination, |
| usage: "transfer_dst", |
| }); |
| } |
| |
| if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 { |
| // VUID-VkCopyImageToBufferInfo2-srcImage-01998 |
| if !src_image |
| .format_features() |
| .intersects(FormatFeatures::TRANSFER_SRC) |
| { |
| return Err(CopyError::MissingFormatFeature { |
| resource: CopyErrorResource::Source, |
| format_feature: "transfer_src", |
| }); |
| } |
| } |
| |
| // VUID-VkCopyImageToBufferInfo2-srcImage-00188 |
| if src_image.samples() != SampleCount::Sample1 { |
| return Err(CopyError::SampleCountInvalid { |
| resource: CopyErrorResource::Source, |
| sample_count: src_image.samples(), |
| allowed_sample_counts: SampleCounts::SAMPLE_1, |
| }); |
| } |
| |
| // VUID-VkCopyImageToBufferInfo2-srcImageLayout-01397 |
| if !matches!( |
| src_image_layout, |
| ImageLayout::TransferSrcOptimal | ImageLayout::General |
| ) { |
| return Err(CopyError::ImageLayoutInvalid { |
| resource: CopyErrorResource::Source, |
| image_layout: src_image_layout, |
| }); |
| } |
| |
| let extent_alignment = match queue_family_properties.min_image_transfer_granularity { |
| [0, 0, 0] => None, |
| min_image_transfer_granularity => { |
| let granularity = move |block_extent: [u32; 3], is_multi_plane: bool| { |
| if is_multi_plane { |
| // Assume planes always have 1x1 blocks |
| min_image_transfer_granularity |
| } else { |
| // "The value returned in minImageTransferGranularity has a unit of |
| // compressed texel blocks for images having a block-compressed format, and |
| // a unit of texels otherwise." |
| [ |
| min_image_transfer_granularity[0] * block_extent[0], |
| min_image_transfer_granularity[1] * block_extent[1], |
| min_image_transfer_granularity[2] * block_extent[2], |
| ] |
| } |
| }; |
| |
| Some(granularity( |
| src_image.format().block_extent(), |
| image_aspects.intersects(ImageAspects::PLANE_0), |
| )) |
| } |
| }; |
| |
| if image_aspects.intersects(ImageAspects::PLANE_0) { |
| // VUID-VkCopyImageToBufferInfo2-aspectMask-01560 |
| image_aspects -= ImageAspects::COLOR; |
| } |
| |
| for (region_index, region) in regions.iter().enumerate() { |
| let &BufferImageCopy { |
| buffer_offset, |
| buffer_row_length, |
| buffer_image_height, |
| ref image_subresource, |
| image_offset, |
| image_extent, |
| _ne: _, |
| } = region; |
| |
| // VUID-VkCopyImageToBufferInfo2-imageSubresource-01703 |
| if image_subresource.mip_level >= src_image.mip_levels() { |
| return Err(CopyError::MipLevelsOutOfRange { |
| resource: CopyErrorResource::Source, |
| region_index, |
| mip_levels_range_end: image_subresource.mip_level + 1, |
| image_mip_levels: src_image.mip_levels(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-layerCount-01700 |
| assert!(!image_subresource.array_layers.is_empty()); |
| |
| // VUID-VkCopyImageToBufferInfo2-imageSubresource-01704 |
| // VUID-VkCopyImageToBufferInfo2-baseArrayLayer-00213 |
| if image_subresource.array_layers.end > src_image.dimensions().array_layers() { |
| return Err(CopyError::ArrayLayersOutOfRange { |
| resource: CopyErrorResource::Source, |
| region_index, |
| array_layers_range_end: image_subresource.array_layers.end, |
| image_array_layers: src_image.dimensions().array_layers(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask |
| assert!(!image_subresource.aspects.is_empty()); |
| |
| // VUID-VkCopyImageToBufferInfo2-aspectMask-00211 |
| if !image_aspects.contains(image_subresource.aspects) { |
| return Err(CopyError::AspectsNotAllowed { |
| resource: CopyErrorResource::Source, |
| region_index, |
| aspects: image_subresource.aspects, |
| allowed_aspects: image_aspects, |
| }); |
| } |
| |
| // VUID-VkBufferImageCopy2-aspectMask-00212 |
| if image_subresource.aspects.count() != 1 { |
| return Err(CopyError::MultipleAspectsNotAllowed { |
| resource: CopyErrorResource::Source, |
| region_index, |
| aspects: image_subresource.aspects, |
| }); |
| } |
| |
| let (image_subresource_format, image_subresource_extent) = |
| if image_aspects.intersects(ImageAspects::PLANE_0) { |
| if image_subresource.aspects.intersects(ImageAspects::PLANE_0) { |
| ( |
| src_image.format().planes()[0], |
| src_image.dimensions().width_height_depth(), |
| ) |
| } else if image_subresource.aspects.intersects(ImageAspects::PLANE_1) { |
| ( |
| src_image.format().planes()[1], |
| src_image |
| .format() |
| .ycbcr_chroma_sampling() |
| .unwrap() |
| .subsampled_extent(src_image.dimensions().width_height_depth()), |
| ) |
| } else { |
| ( |
| src_image.format().planes()[2], |
| src_image |
| .format() |
| .ycbcr_chroma_sampling() |
| .unwrap() |
| .subsampled_extent(src_image.dimensions().width_height_depth()), |
| ) |
| } |
| } else { |
| ( |
| src_image.format(), |
| src_image |
| .dimensions() |
| .mip_level_dimensions(image_subresource.mip_level) |
| .unwrap() |
| .width_height_depth(), |
| ) |
| }; |
| |
| if let Some(extent_alignment) = extent_alignment { |
| for i in 0..3 { |
| // VUID-VkBufferImageCopy2-imageExtent-06659 |
| // VUID-VkBufferImageCopy2-imageExtent-06660 |
| // VUID-VkBufferImageCopy2-imageExtent-06661 |
| assert!(image_extent[i] != 0); |
| |
| // VUID-VkCopyImageToBufferInfo2-imageOffset-00197 |
| // VUID-VkCopyImageToBufferInfo2-imageOffset-00198 |
| // VUID-VkCopyImageToBufferInfo2-imageOffset-00200 |
| if image_offset[i] + image_extent[i] > image_subresource_extent[i] { |
| return Err(CopyError::RegionOutOfImageBounds { |
| resource: CopyErrorResource::Source, |
| region_index, |
| offset_range_end: [ |
| image_offset[0] + image_extent[0], |
| image_offset[1] + image_extent[1], |
| image_offset[2] + image_extent[2], |
| ], |
| subresource_extent: image_subresource_extent, |
| }); |
| } |
| |
| // VUID-VkCopyImageToBufferInfo2-imageOffset-01794 |
| // VUID-VkCopyImageToBufferInfo2-imageOffset-00205 |
| if image_offset[i] % extent_alignment[i] != 0 { |
| return Err(CopyError::OffsetNotAlignedForImage { |
| resource: CopyErrorResource::Source, |
| region_index, |
| offset: image_offset, |
| required_alignment: extent_alignment, |
| }); |
| } |
| |
| // VUID-VkCopyImageToBufferInfo2-imageOffset-01794 |
| // VUID-VkCopyImageToBufferInfo2-imageExtent-00207 |
| // VUID-VkCopyImageToBufferInfo2-imageExtent-00208 |
| // VUID-VkCopyImageToBufferInfo2-imageExtent-00209 |
| if image_offset[i] + image_extent[i] != image_subresource_extent[i] |
| && image_extent[i] % extent_alignment[i] != 0 |
| { |
| return Err(CopyError::ExtentNotAlignedForImage { |
| resource: CopyErrorResource::Source, |
| region_index, |
| extent: image_extent, |
| required_alignment: extent_alignment, |
| }); |
| } |
| } |
| } else { |
| // If granularity is `None`, then we can only copy whole subresources. |
| |
| // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 |
| if image_offset != [0, 0, 0] { |
| return Err(CopyError::OffsetNotAlignedForImage { |
| resource: CopyErrorResource::Source, |
| region_index, |
| offset: image_offset, |
| required_alignment: image_subresource_extent, |
| }); |
| } |
| |
| // VUID-VkCopyBufferToImageInfo2-imageOffset-01793 |
| if image_extent != image_subresource_extent { |
| return Err(CopyError::ExtentNotAlignedForImage { |
| resource: CopyErrorResource::Source, |
| region_index, |
| extent: image_extent, |
| required_alignment: image_subresource_extent, |
| }); |
| } |
| } |
| |
| // VUID-VkBufferImageCopy2-bufferRowLength-00195 |
| if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) { |
| return Err(CopyError::BufferRowLengthTooSmall { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| row_length: buffer_row_length, |
| min: image_extent[0], |
| }); |
| } |
| |
| // VUID-VkBufferImageCopy2-bufferImageHeight-00196 |
| if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) { |
| return Err(CopyError::BufferImageHeightTooSmall { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| image_height: buffer_image_height, |
| min: image_extent[1], |
| }); |
| } |
| |
| let image_subresource_block_extent = image_subresource_format.block_extent(); |
| |
| // VUID-VkCopyImageToBufferInfo2-bufferRowLength-00203 |
| if buffer_row_length % image_subresource_block_extent[0] != 0 { |
| return Err(CopyError::BufferRowLengthNotAligned { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| row_length: buffer_row_length, |
| required_alignment: image_subresource_block_extent[0], |
| }); |
| } |
| |
| // VUID-VkCopyImageToBufferInfo2-bufferImageHeight-00204 |
| if buffer_image_height % image_subresource_block_extent[1] != 0 { |
| return Err(CopyError::BufferImageHeightNotAligned { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| image_height: buffer_image_height, |
| required_alignment: image_subresource_block_extent[1], |
| }); |
| } |
| |
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferImageCopy.html#_description |
| let image_subresource_block_size = |
| if image_subresource.aspects.intersects(ImageAspects::STENCIL) { |
| 1 |
| } else if image_subresource.aspects.intersects(ImageAspects::DEPTH) { |
| match image_subresource_format { |
| Format::D16_UNORM | Format::D16_UNORM_S8_UINT => 2, |
| Format::D32_SFLOAT |
| | Format::D32_SFLOAT_S8_UINT |
| | Format::X8_D24_UNORM_PACK32 |
| | Format::D24_UNORM_S8_UINT => 4, |
| _ => unreachable!(), |
| } |
| } else { |
| image_subresource_format.block_size().unwrap() |
| }; |
| |
| // VUID-VkCopyImageToBufferInfo2-pRegions-04725 |
| // VUID-VkCopyImageToBufferInfo2-pRegions-04726 |
| if (buffer_row_length / image_subresource_block_extent[0]) as DeviceSize |
| * image_subresource_block_size |
| > 0x7FFFFFFF |
| { |
| return Err(CopyError::BufferRowLengthTooLarge { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| buffer_row_length, |
| }); |
| } |
| |
| let buffer_offset_alignment = |
| if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) { |
| 4 |
| } else { |
| let mut buffer_offset_alignment = image_subresource_block_size; |
| |
| // VUID-VkCopyImageToBufferInfo2-commandBuffer-04052 |
| // Make the alignment a multiple of 4. |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE) |
| { |
| if buffer_offset_alignment % 2 != 0 { |
| buffer_offset_alignment *= 2; |
| } |
| |
| if buffer_offset_alignment % 4 != 0 { |
| buffer_offset_alignment *= 2; |
| } |
| } |
| |
| buffer_offset_alignment |
| }; |
| |
| // VUID-VkCopyImageToBufferInfo2-bufferOffset-01558 |
| // VUID-VkCopyImageToBufferInfo2-bufferOffset-01559 |
| // VUID-VkCopyImageToBufferInfo2-bufferOffset-00206 |
| // VUID-VkCopyImageToBufferInfo2-srcImage-04053 |
| if (dst_buffer.offset() + buffer_offset) % buffer_offset_alignment != 0 { |
| return Err(CopyError::OffsetNotAlignedForBuffer { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| offset: dst_buffer.offset() + buffer_offset, |
| required_alignment: buffer_offset_alignment, |
| }); |
| } |
| |
| let buffer_copy_size = region.buffer_copy_size(image_subresource_format); |
| |
| // VUID-VkCopyImageToBufferInfo2-pRegions-00183 |
| if buffer_offset + buffer_copy_size > dst_buffer.size() { |
| return Err(CopyError::RegionOutOfBufferBounds { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| offset_range_end: buffer_offset + buffer_copy_size, |
| buffer_size: dst_buffer.size(), |
| }); |
| } |
| } |
| |
| // VUID-VkCopyImageToBufferInfo2-pRegions-00184 |
| // Can't occur as long as memory aliasing isn't allowed. |
| |
| Ok(()) |
| } |
| |
| /// Blits an image to another. |
| /// |
| /// A *blit* is similar to an image copy operation, except that the portion of the image that |
| /// is transferred can be resized. You choose an area of the source and an area of the |
| /// destination, and the implementation will resize the area of the source so that it matches |
| /// the size of the area of the destination before writing it. |
| /// |
| /// Blit operations have several restrictions: |
| /// |
| /// - Blit operations are only allowed on queue families that support graphics operations. |
| /// - The format of the source and destination images must support blit operations, which |
| /// depends on the Vulkan implementation. Vulkan guarantees that some specific formats must |
| /// always be supported. See tables 52 to 61 of the specifications. |
| /// - Only single-sampled images are allowed. |
| /// - You can only blit between two images whose formats belong to the same type. The types |
| /// are: floating-point, signed integers, unsigned integers, depth-stencil. |
| /// - If you blit between depth, stencil or depth-stencil images, the format of both images |
| /// must match exactly. |
| /// - If you blit between depth, stencil or depth-stencil images, only the `Nearest` filter is |
| /// allowed. |
| /// - For two-dimensional images, the Z coordinate must be 0 for the top-left offset and 1 for |
| /// the bottom-right offset. Same for the Y coordinate for one-dimensional images. |
| /// - For non-array images, the base array layer must be 0 and the number of layers must be 1. |
| /// |
| /// If `layer_count` is greater than 1, the blit will happen between each individual layer as |
| /// if they were separate images. |
| /// |
| /// # Panics |
| /// |
| /// - Panics if the source or the destination was not created with `device`. |
| pub fn blit_image(&mut self, blit_image_info: BlitImageInfo) -> Result<&mut Self, CopyError> { |
| self.validate_blit_image(&blit_image_info)?; |
| |
| unsafe { |
| self.inner.blit_image(blit_image_info)?; |
| } |
| |
| Ok(self) |
| } |
| |
| fn validate_blit_image(&self, blit_image_info: &BlitImageInfo) -> Result<(), CopyError> { |
| let device = self.device(); |
| |
| // VUID-vkCmdBlitImage2-renderpass |
| if self.render_pass_state.is_some() { |
| return Err(CopyError::ForbiddenInsideRenderPass); |
| } |
| |
| let queue_family_properties = self.queue_family_properties(); |
| |
| // VUID-vkCmdBlitImage2-commandBuffer-cmdpool |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::GRAPHICS) |
| { |
| return Err(CopyError::NotSupportedByQueueFamily); |
| } |
| |
| let &BlitImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| filter, |
| _ne: _, |
| } = blit_image_info; |
| |
| // VUID-VkBlitImageInfo2-srcImageLayout-parameter |
| src_image_layout.validate_device(device)?; |
| |
| // VUID-VkBlitImageInfo2-dstImageLayout-parameter |
| dst_image_layout.validate_device(device)?; |
| |
| // VUID-VkBlitImageInfo2-filter-parameter |
| filter.validate_device(device)?; |
| |
| let src_image_inner = src_image.inner(); |
| let dst_image_inner = dst_image.inner(); |
| |
| // VUID-VkBlitImageInfo2-commonparent |
| assert_eq!(device, src_image.device()); |
| assert_eq!(device, dst_image.device()); |
| |
| let src_image_aspects = src_image.format().aspects(); |
| let dst_image_aspects = dst_image.format().aspects(); |
| let src_image_type = src_image.dimensions().image_type(); |
| let dst_image_type = dst_image.dimensions().image_type(); |
| |
| // VUID-VkBlitImageInfo2-srcImage-00219 |
| if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Source, |
| usage: "transfer_src", |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-dstImage-00224 |
| if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) { |
| return Err(CopyError::MissingUsage { |
| resource: CopyErrorResource::Destination, |
| usage: "transfer_dst", |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-srcImage-01999 |
| if !src_image |
| .format_features() |
| .intersects(FormatFeatures::BLIT_SRC) |
| { |
| return Err(CopyError::MissingFormatFeature { |
| resource: CopyErrorResource::Source, |
| format_feature: "blit_src", |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-dstImage-02000 |
| if !dst_image |
| .format_features() |
| .intersects(FormatFeatures::BLIT_DST) |
| { |
| return Err(CopyError::MissingFormatFeature { |
| resource: CopyErrorResource::Destination, |
| format_feature: "blit_dst", |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-srcImage-06421 |
| if src_image.format().ycbcr_chroma_sampling().is_some() { |
| return Err(CopyError::FormatNotSupported { |
| resource: CopyErrorResource::Source, |
| format: src_image.format(), |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-dstImage-06422 |
| if dst_image.format().ycbcr_chroma_sampling().is_some() { |
| return Err(CopyError::FormatNotSupported { |
| resource: CopyErrorResource::Destination, |
| format: src_image.format(), |
| }); |
| } |
| |
| if !(src_image_aspects.intersects(ImageAspects::COLOR) |
| && dst_image_aspects.intersects(ImageAspects::COLOR)) |
| { |
| // VUID-VkBlitImageInfo2-srcImage-00231 |
| if src_image.format() != dst_image.format() { |
| return Err(CopyError::FormatsMismatch { |
| src_format: src_image.format(), |
| dst_format: dst_image.format(), |
| }); |
| } |
| } else { |
| // VUID-VkBlitImageInfo2-srcImage-00229 |
| // VUID-VkBlitImageInfo2-srcImage-00230 |
| if !matches!( |
| ( |
| src_image.format().type_color().unwrap(), |
| dst_image.format().type_color().unwrap() |
| ), |
| ( |
| NumericType::SFLOAT |
| | NumericType::UFLOAT |
| | NumericType::SNORM |
| | NumericType::UNORM |
| | NumericType::SSCALED |
| | NumericType::USCALED |
| | NumericType::SRGB, |
| NumericType::SFLOAT |
| | NumericType::UFLOAT |
| | NumericType::SNORM |
| | NumericType::UNORM |
| | NumericType::SSCALED |
| | NumericType::USCALED |
| | NumericType::SRGB, |
| ) | (NumericType::SINT, NumericType::SINT) |
| | (NumericType::UINT, NumericType::UINT) |
| ) { |
| return Err(CopyError::FormatsNotCompatible { |
| src_format: src_image.format(), |
| dst_format: dst_image.format(), |
| }); |
| } |
| } |
| |
| // VUID-VkBlitImageInfo2-srcImage-00233 |
| if src_image.samples() != SampleCount::Sample1 { |
| return Err(CopyError::SampleCountInvalid { |
| resource: CopyErrorResource::Destination, |
| sample_count: dst_image.samples(), |
| allowed_sample_counts: SampleCounts::SAMPLE_1, |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-dstImage-00234 |
| if dst_image.samples() != SampleCount::Sample1 { |
| return Err(CopyError::SampleCountInvalid { |
| resource: CopyErrorResource::Destination, |
| sample_count: dst_image.samples(), |
| allowed_sample_counts: SampleCounts::SAMPLE_1, |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-srcImageLayout-01398 |
| if !matches!( |
| src_image_layout, |
| ImageLayout::TransferSrcOptimal | ImageLayout::General |
| ) { |
| return Err(CopyError::ImageLayoutInvalid { |
| resource: CopyErrorResource::Source, |
| image_layout: src_image_layout, |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-dstImageLayout-01399 |
| if !matches!( |
| dst_image_layout, |
| ImageLayout::TransferDstOptimal | ImageLayout::General |
| ) { |
| return Err(CopyError::ImageLayoutInvalid { |
| resource: CopyErrorResource::Destination, |
| image_layout: dst_image_layout, |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-srcImage-00232 |
| if !src_image_aspects.intersects(ImageAspects::COLOR) && filter != Filter::Nearest { |
| return Err(CopyError::FilterNotSupportedByFormat); |
| } |
| |
| match filter { |
| Filter::Nearest => (), |
| Filter::Linear => { |
| // VUID-VkBlitImageInfo2-filter-02001 |
| if !src_image |
| .format_features() |
| .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR) |
| { |
| return Err(CopyError::FilterNotSupportedByFormat); |
| } |
| } |
| Filter::Cubic => { |
| // VUID-VkBlitImageInfo2-filter-02002 |
| if !src_image |
| .format_features() |
| .intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC) |
| { |
| return Err(CopyError::FilterNotSupportedByFormat); |
| } |
| |
| // VUID-VkBlitImageInfo2-filter-00237 |
| if !matches!(src_image.dimensions(), ImageDimensions::Dim2d { .. }) { |
| return Err(CopyError::FilterNotSupportedForImageType); |
| } |
| } |
| } |
| |
| let same_image = src_image_inner.image == dst_image_inner.image; |
| let mut overlap_subresource_indices = None; |
| let mut overlap_extent_indices = None; |
| |
| for (region_index, region) in regions.iter().enumerate() { |
| let &ImageBlit { |
| ref src_subresource, |
| src_offsets, |
| ref dst_subresource, |
| dst_offsets, |
| _ne: _, |
| } = region; |
| |
| let check_subresource = |resource: CopyErrorResource, |
| image: &dyn ImageAccess, |
| image_aspects: ImageAspects, |
| subresource: &ImageSubresourceLayers| |
| -> Result<_, CopyError> { |
| // VUID-VkBlitImageInfo2-srcSubresource-01705 |
| // VUID-VkBlitImageInfo2-dstSubresource-01706 |
| if subresource.mip_level >= image.mip_levels() { |
| return Err(CopyError::MipLevelsOutOfRange { |
| resource, |
| region_index, |
| mip_levels_range_end: subresource.mip_level + 1, |
| image_mip_levels: image.mip_levels(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-layerCount-01700 |
| assert!(!subresource.array_layers.is_empty()); |
| |
| // VUID-VkBlitImageInfo2-srcSubresource-01707 |
| // VUID-VkBlitImageInfo2-dstSubresource-01708 |
| // VUID-VkBlitImageInfo2-srcImage-00240 |
| if subresource.array_layers.end > image.dimensions().array_layers() { |
| return Err(CopyError::ArrayLayersOutOfRange { |
| resource, |
| region_index, |
| array_layers_range_end: subresource.array_layers.end, |
| image_array_layers: image.dimensions().array_layers(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-aspectMask-parameter |
| subresource.aspects.validate_device(device)?; |
| |
| // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask |
| assert!(!subresource.aspects.is_empty()); |
| |
| // VUID-VkBlitImageInfo2-aspectMask-00241 |
| // VUID-VkBlitImageInfo2-aspectMask-00242 |
| if !image_aspects.contains(subresource.aspects) { |
| return Err(CopyError::AspectsNotAllowed { |
| resource, |
| region_index, |
| aspects: subresource.aspects, |
| allowed_aspects: image_aspects, |
| }); |
| } |
| |
| Ok(image |
| .dimensions() |
| .mip_level_dimensions(subresource.mip_level) |
| .unwrap() |
| .width_height_depth()) |
| }; |
| |
| let src_subresource_extent = check_subresource( |
| CopyErrorResource::Source, |
| src_image, |
| src_image_aspects, |
| src_subresource, |
| )?; |
| let dst_subresource_extent = check_subresource( |
| CopyErrorResource::Destination, |
| dst_image, |
| dst_image_aspects, |
| dst_subresource, |
| )?; |
| |
| // VUID-VkImageBlit2-aspectMask-00238 |
| if src_subresource.aspects != dst_subresource.aspects { |
| return Err(CopyError::AspectsMismatch { |
| region_index, |
| src_aspects: src_subresource.aspects, |
| dst_aspects: dst_subresource.aspects, |
| }); |
| } |
| |
| let src_layer_count = |
| src_subresource.array_layers.end - src_subresource.array_layers.start; |
| let dst_layer_count = |
| dst_subresource.array_layers.end - dst_subresource.array_layers.start; |
| |
| // VUID-VkImageBlit2-layerCount-00239 |
| // VUID-VkBlitImageInfo2-srcImage-00240 |
| if src_layer_count != dst_layer_count { |
| return Err(CopyError::ArrayLayerCountMismatch { |
| region_index, |
| src_layer_count, |
| dst_layer_count, |
| }); |
| } |
| |
| let check_offset_extent = |resource: CopyErrorResource, |
| image_type: ImageType, |
| subresource_extent: [u32; 3], |
| offsets: [[u32; 3]; 2]| |
| -> Result<_, CopyError> { |
| match image_type { |
| ImageType::Dim1d => { |
| // VUID-VkBlitImageInfo2-srcImage-00245 |
| // VUID-VkBlitImageInfo2-dstImage-00250 |
| if !(offsets[0][1] == 0 && offsets[1][1] == 1) { |
| return Err(CopyError::OffsetsInvalidForImageType { |
| resource, |
| region_index, |
| offsets: [offsets[0][1], offsets[1][1]], |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-srcImage-00247 |
| // VUID-VkBlitImageInfo2-dstImage-00252 |
| if !(offsets[0][2] == 0 && offsets[1][2] == 1) { |
| return Err(CopyError::OffsetsInvalidForImageType { |
| resource, |
| region_index, |
| offsets: [offsets[0][2], offsets[1][2]], |
| }); |
| } |
| } |
| ImageType::Dim2d => { |
| // VUID-VkBlitImageInfo2-srcImage-00247 |
| // VUID-VkBlitImageInfo2-dstImage-00252 |
| if !(offsets[0][2] == 0 && offsets[1][2] == 1) { |
| return Err(CopyError::OffsetsInvalidForImageType { |
| resource, |
| region_index, |
| offsets: [offsets[0][2], offsets[1][2]], |
| }); |
| } |
| } |
| ImageType::Dim3d => (), |
| } |
| |
| let offset_range_end = [ |
| max(offsets[0][0], offsets[1][0]), |
| max(offsets[0][1], offsets[1][1]), |
| max(offsets[0][2], offsets[1][2]), |
| ]; |
| |
| for i in 0..3 { |
| // VUID-VkBlitImageInfo2-srcOffset-00243 |
| // VUID-VkBlitImageInfo2-srcOffset-00244 |
| // VUID-VkBlitImageInfo2-srcOffset-00246 |
| // VUID-VkBlitImageInfo2-dstOffset-00248 |
| // VUID-VkBlitImageInfo2-dstOffset-00249 |
| // VUID-VkBlitImageInfo2-dstOffset-00251 |
| if offset_range_end[i] > subresource_extent[i] { |
| return Err(CopyError::RegionOutOfImageBounds { |
| resource, |
| region_index, |
| offset_range_end, |
| subresource_extent, |
| }); |
| } |
| } |
| |
| Ok(()) |
| }; |
| |
| check_offset_extent( |
| CopyErrorResource::Source, |
| src_image_type, |
| src_subresource_extent, |
| src_offsets, |
| )?; |
| check_offset_extent( |
| CopyErrorResource::Destination, |
| dst_image_type, |
| dst_subresource_extent, |
| dst_offsets, |
| )?; |
| |
| // VUID-VkBlitImageInfo2-pRegions-00217 |
| if same_image { |
| let src_region_index = region_index; |
| let src_subresource_axes = [ |
| src_image_inner.first_mipmap_level + src_subresource.mip_level |
| ..src_image_inner.first_mipmap_level + src_subresource.mip_level + 1, |
| src_image_inner.first_layer + src_subresource.array_layers.start |
| ..src_image_inner.first_layer + src_subresource.array_layers.end, |
| ]; |
| let src_extent_axes = [ |
| min(src_offsets[0][0], src_offsets[1][0]) |
| ..max(src_offsets[0][0], src_offsets[1][0]), |
| min(src_offsets[0][1], src_offsets[1][1]) |
| ..max(src_offsets[0][1], src_offsets[1][1]), |
| min(src_offsets[0][2], src_offsets[1][2]) |
| ..max(src_offsets[0][2], src_offsets[1][2]), |
| ]; |
| |
| for (dst_region_index, dst_region) in regions.iter().enumerate() { |
| let &ImageBlit { |
| ref dst_subresource, |
| dst_offsets, |
| .. |
| } = dst_region; |
| |
| let dst_subresource_axes = [ |
| dst_image_inner.first_mipmap_level + dst_subresource.mip_level |
| ..dst_image_inner.first_mipmap_level + dst_subresource.mip_level + 1, |
| dst_image_inner.first_layer + src_subresource.array_layers.start |
| ..dst_image_inner.first_layer + src_subresource.array_layers.end, |
| ]; |
| |
| if src_subresource_axes.iter().zip(dst_subresource_axes).any( |
| |(src_range, dst_range)| { |
| src_range.start >= dst_range.end || dst_range.start >= src_range.end |
| }, |
| ) { |
| continue; |
| } |
| |
| // If the subresource axes all overlap, then the source and destination must |
| // have the same layout. |
| overlap_subresource_indices = Some((src_region_index, dst_region_index)); |
| |
| let dst_extent_axes = [ |
| min(dst_offsets[0][0], dst_offsets[1][0]) |
| ..max(dst_offsets[0][0], dst_offsets[1][0]), |
| min(dst_offsets[0][1], dst_offsets[1][1]) |
| ..max(dst_offsets[0][1], dst_offsets[1][1]), |
| min(dst_offsets[0][2], dst_offsets[1][2]) |
| ..max(dst_offsets[0][2], dst_offsets[1][2]), |
| ]; |
| |
| if src_extent_axes |
| .iter() |
| .zip(dst_extent_axes) |
| .any(|(src_range, dst_range)| { |
| src_range.start >= dst_range.end || dst_range.start >= src_range.end |
| }) |
| { |
| continue; |
| } |
| |
| // If the extent axes *also* overlap, then that's an error. |
| overlap_extent_indices = Some((src_region_index, dst_region_index)); |
| } |
| } |
| } |
| |
| // VUID-VkBlitImageInfo2-pRegions-00217 |
| if let Some((src_region_index, dst_region_index)) = overlap_extent_indices { |
| return Err(CopyError::OverlappingRegions { |
| src_region_index, |
| dst_region_index, |
| }); |
| } |
| |
| // VUID-VkBlitImageInfo2-srcImageLayout-00221 |
| // VUID-VkBlitImageInfo2-dstImageLayout-00226 |
| if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices { |
| if src_image_layout != dst_image_layout { |
| return Err(CopyError::OverlappingSubresourcesLayoutMismatch { |
| src_region_index, |
| dst_region_index, |
| src_image_layout, |
| dst_image_layout, |
| }); |
| } |
| } |
| |
| Ok(()) |
| } |
| |
| /// Resolves a multisampled image into a single-sampled image. |
| /// |
| /// # Panics |
| /// |
| /// - Panics if `src_image` or `dst_image` were not created from the same device |
| /// as `self`. |
| pub fn resolve_image( |
| &mut self, |
| resolve_image_info: ResolveImageInfo, |
| ) -> Result<&mut Self, CopyError> { |
| self.validate_resolve_image(&resolve_image_info)?; |
| |
| unsafe { |
| self.inner.resolve_image(resolve_image_info)?; |
| } |
| |
| Ok(self) |
| } |
| |
| fn validate_resolve_image( |
| &self, |
| resolve_image_info: &ResolveImageInfo, |
| ) -> Result<(), CopyError> { |
| let device = self.device(); |
| |
| // VUID-vkCmdResolveImage2-renderpass |
| if self.render_pass_state.is_some() { |
| return Err(CopyError::ForbiddenInsideRenderPass); |
| } |
| |
| let queue_family_properties = self.queue_family_properties(); |
| |
| // VUID-vkCmdResolveImage2-commandBuffer-cmdpool |
| if !queue_family_properties |
| .queue_flags |
| .intersects(QueueFlags::GRAPHICS) |
| { |
| return Err(CopyError::NotSupportedByQueueFamily); |
| } |
| |
| let &ResolveImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = resolve_image_info; |
| |
| // VUID-VkResolveImageInfo2-srcImageLayout-parameter |
| src_image_layout.validate_device(device)?; |
| |
| // VUID-VkResolveImageInfo2-dstImageLayout-parameter |
| dst_image_layout.validate_device(device)?; |
| |
| // VUID-VkResolveImageInfo2-commonparent |
| assert_eq!(device, src_image.device()); |
| assert_eq!(device, dst_image.device()); |
| |
| let src_image_type = src_image.dimensions().image_type(); |
| let dst_image_type = dst_image.dimensions().image_type(); |
| |
| // VUID-VkResolveImageInfo2-srcImage-00257 |
| if src_image.samples() == SampleCount::Sample1 { |
| return Err(CopyError::SampleCountInvalid { |
| resource: CopyErrorResource::Source, |
| sample_count: dst_image.samples(), |
| allowed_sample_counts: SampleCounts::SAMPLE_2 |
| | SampleCounts::SAMPLE_4 |
| | SampleCounts::SAMPLE_8 |
| | SampleCounts::SAMPLE_16 |
| | SampleCounts::SAMPLE_32 |
| | SampleCounts::SAMPLE_64, |
| }); |
| } |
| |
| // VUID-VkResolveImageInfo2-dstImage-00259 |
| if dst_image.samples() != SampleCount::Sample1 { |
| return Err(CopyError::SampleCountInvalid { |
| resource: CopyErrorResource::Destination, |
| sample_count: dst_image.samples(), |
| allowed_sample_counts: SampleCounts::SAMPLE_1, |
| }); |
| } |
| |
| // VUID-VkResolveImageInfo2-dstImage-02003 |
| if !dst_image |
| .format_features() |
| .intersects(FormatFeatures::COLOR_ATTACHMENT) |
| { |
| return Err(CopyError::MissingFormatFeature { |
| resource: CopyErrorResource::Destination, |
| format_feature: "color_attachment", |
| }); |
| } |
| |
| // VUID-VkResolveImageInfo2-srcImage-01386 |
| if src_image.format() != dst_image.format() { |
| return Err(CopyError::FormatsMismatch { |
| src_format: src_image.format(), |
| dst_format: dst_image.format(), |
| }); |
| } |
| |
| // VUID-VkResolveImageInfo2-srcImageLayout-01400 |
| if !matches!( |
| src_image_layout, |
| ImageLayout::TransferSrcOptimal | ImageLayout::General |
| ) { |
| return Err(CopyError::ImageLayoutInvalid { |
| resource: CopyErrorResource::Source, |
| image_layout: src_image_layout, |
| }); |
| } |
| |
| // VUID-VkResolveImageInfo2-dstImageLayout-01401 |
| if !matches!( |
| dst_image_layout, |
| ImageLayout::TransferDstOptimal | ImageLayout::General |
| ) { |
| return Err(CopyError::ImageLayoutInvalid { |
| resource: CopyErrorResource::Destination, |
| image_layout: dst_image_layout, |
| }); |
| } |
| |
| // Should be guaranteed by the requirement that formats match, and that the destination |
| // image format features support color attachments. |
| debug_assert!( |
| src_image.format().aspects().intersects(ImageAspects::COLOR) |
| && dst_image.format().aspects().intersects(ImageAspects::COLOR) |
| ); |
| |
| for (region_index, region) in regions.iter().enumerate() { |
| let &ImageResolve { |
| ref src_subresource, |
| src_offset, |
| ref dst_subresource, |
| dst_offset, |
| extent, |
| _ne: _, |
| } = region; |
| |
| let check_subresource = |resource: CopyErrorResource, |
| image: &dyn ImageAccess, |
| subresource: &ImageSubresourceLayers| |
| -> Result<_, CopyError> { |
| // VUID-VkResolveImageInfo2-srcSubresource-01709 |
| // VUID-VkResolveImageInfo2-dstSubresource-01710 |
| if subresource.mip_level >= image.mip_levels() { |
| return Err(CopyError::MipLevelsOutOfRange { |
| resource, |
| region_index, |
| mip_levels_range_end: subresource.mip_level + 1, |
| image_mip_levels: image.mip_levels(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-layerCount-01700 |
| // VUID-VkResolveImageInfo2-srcImage-04446 |
| // VUID-VkResolveImageInfo2-srcImage-04447 |
| assert!(!subresource.array_layers.is_empty()); |
| |
| // VUID-VkResolveImageInfo2-srcSubresource-01711 |
| // VUID-VkResolveImageInfo2-dstSubresource-01712 |
| // VUID-VkResolveImageInfo2-srcImage-04446 |
| // VUID-VkResolveImageInfo2-srcImage-04447 |
| if subresource.array_layers.end > image.dimensions().array_layers() { |
| return Err(CopyError::ArrayLayersOutOfRange { |
| resource: CopyErrorResource::Destination, |
| region_index, |
| array_layers_range_end: subresource.array_layers.end, |
| image_array_layers: image.dimensions().array_layers(), |
| }); |
| } |
| |
| // VUID-VkImageSubresourceLayers-aspectMask-parameter |
| subresource.aspects.validate_device(device)?; |
| |
| // VUID-VkImageSubresourceLayers-aspectMask-requiredbitmask |
| // VUID-VkImageResolve2-aspectMask-00266 |
| if subresource.aspects != (ImageAspects::COLOR) { |
| return Err(CopyError::AspectsNotAllowed { |
| resource, |
| region_index, |
| aspects: subresource.aspects, |
| allowed_aspects: ImageAspects::COLOR, |
| }); |
| } |
| |
| Ok(image |
| .dimensions() |
| .mip_level_dimensions(subresource.mip_level) |
| .unwrap() |
| .width_height_depth()) |
| }; |
| |
| let src_subresource_extent = |
| check_subresource(CopyErrorResource::Source, src_image, src_subresource)?; |
| let dst_subresource_extent = |
| check_subresource(CopyErrorResource::Destination, dst_image, dst_subresource)?; |
| |
| let src_layer_count = |
| src_subresource.array_layers.end - src_subresource.array_layers.start; |
| let dst_layer_count = |
| dst_subresource.array_layers.end - dst_subresource.array_layers.start; |
| |
| // VUID-VkImageResolve2-layerCount-00267 |
| // VUID-VkResolveImageInfo2-srcImage-04446 |
| // VUID-VkResolveImageInfo2-srcImage-04447 |
| if src_layer_count != dst_layer_count { |
| return Err(CopyError::ArrayLayerCountMismatch { |
| region_index, |
| src_layer_count, |
| dst_layer_count, |
| }); |
| } |
| |
| // No VUID, but it makes sense? |
| assert!(extent[0] != 0 && extent[1] != 0 && extent[2] != 0); |
| |
| let check_offset_extent = |resource: CopyErrorResource, |
| _image_type: ImageType, |
| subresource_extent: [u32; 3], |
| offset: [u32; 3]| |
| -> Result<_, CopyError> { |
| for i in 0..3 { |
| // No VUID, but makes sense? |
| assert!(extent[i] != 0); |
| |
| // VUID-VkResolveImageInfo2-srcOffset-00269 |
| // VUID-VkResolveImageInfo2-srcOffset-00270 |
| // VUID-VkResolveImageInfo2-srcOffset-00272 |
| // VUID-VkResolveImageInfo2-dstOffset-00274 |
| // VUID-VkResolveImageInfo2-dstOffset-00275 |
| // VUID-VkResolveImageInfo2-dstOffset-00277 |
| if offset[i] + extent[i] > subresource_extent[i] { |
| return Err(CopyError::RegionOutOfImageBounds { |
| resource, |
| region_index, |
| offset_range_end: [ |
| offset[0] + extent[0], |
| offset[1] + extent[1], |
| offset[2] + extent[2], |
| ], |
| subresource_extent, |
| }); |
| } |
| } |
| |
| Ok(()) |
| }; |
| |
| check_offset_extent( |
| CopyErrorResource::Source, |
| src_image_type, |
| src_subresource_extent, |
| src_offset, |
| )?; |
| check_offset_extent( |
| CopyErrorResource::Destination, |
| dst_image_type, |
| dst_subresource_extent, |
| dst_offset, |
| )?; |
| } |
| |
| // VUID-VkResolveImageInfo2-pRegions-00255 |
| // Can't occur as long as memory aliasing isn't allowed, because `src_image` and |
| // `dst_image` must have different sample counts and therefore can never be the same image. |
| |
| Ok(()) |
| } |
| } |
| |
| impl SyncCommandBufferBuilder { |
| /// Calls `vkCmdCopyBuffer` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn copy_buffer( |
| &mut self, |
| copy_buffer_info: CopyBufferInfo, |
| ) -> Result<(), SyncCommandBufferBuilderError> { |
| struct Cmd { |
| copy_buffer_info: CopyBufferInfo, |
| } |
| |
| impl Command for Cmd { |
| fn name(&self) -> &'static str { |
| "copy_buffer" |
| } |
| |
| unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { |
| out.copy_buffer(&self.copy_buffer_info); |
| } |
| } |
| |
| let CopyBufferInfo { |
| src_buffer, |
| dst_buffer, |
| regions, |
| _ne: _, |
| } = ©_buffer_info; |
| |
| let command_index = self.commands.len(); |
| let command_name = "copy_buffer"; |
| let resources: SmallVec<[_; 8]> = regions |
| .iter() |
| .flat_map(|region| { |
| let &BufferCopy { |
| src_offset, |
| dst_offset, |
| size, |
| _ne: _, |
| } = region; |
| |
| [ |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Source, |
| secondary_use_ref: None, |
| }, |
| Resource::Buffer { |
| buffer: src_buffer.clone(), |
| range: src_offset..src_offset + size, |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_READ, |
| exclusive: false, |
| }, |
| }, |
| ), |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Destination, |
| secondary_use_ref: None, |
| }, |
| Resource::Buffer { |
| buffer: dst_buffer.clone(), |
| range: dst_offset..dst_offset + size, |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_WRITE, |
| exclusive: true, |
| }, |
| }, |
| ), |
| ] |
| }) |
| .collect(); |
| |
| for resource in &resources { |
| self.check_resource_conflicts(resource)?; |
| } |
| |
| self.commands.push(Box::new(Cmd { copy_buffer_info })); |
| |
| for resource in resources { |
| self.add_resource(resource); |
| } |
| |
| Ok(()) |
| } |
| |
| /// Calls `vkCmdCopyImage` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn copy_image( |
| &mut self, |
| copy_image_info: CopyImageInfo, |
| ) -> Result<(), SyncCommandBufferBuilderError> { |
| struct Cmd { |
| copy_image_info: CopyImageInfo, |
| } |
| |
| impl Command for Cmd { |
| fn name(&self) -> &'static str { |
| "copy_buffer_to_image" |
| } |
| |
| unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { |
| out.copy_image(&self.copy_image_info); |
| } |
| } |
| |
| let &CopyImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = ©_image_info; |
| |
| let command_index = self.commands.len(); |
| let command_name = "copy_image"; |
| let resources: SmallVec<[_; 8]> = regions |
| .iter() |
| .flat_map(|region| { |
| let &ImageCopy { |
| ref src_subresource, |
| src_offset: _, |
| ref dst_subresource, |
| dst_offset: _, |
| extent: _, |
| _ne: _, |
| } = region; |
| |
| [ |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Source, |
| secondary_use_ref: None, |
| }, |
| Resource::Image { |
| image: src_image.clone(), |
| subresource_range: src_subresource.clone().into(), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_READ, |
| exclusive: false, |
| }, |
| start_layout: src_image_layout, |
| end_layout: src_image_layout, |
| }, |
| ), |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Destination, |
| secondary_use_ref: None, |
| }, |
| Resource::Image { |
| image: dst_image.clone(), |
| subresource_range: dst_subresource.clone().into(), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_WRITE, |
| exclusive: true, |
| }, |
| start_layout: dst_image_layout, |
| end_layout: dst_image_layout, |
| }, |
| ), |
| ] |
| }) |
| .collect(); |
| |
| for resource in &resources { |
| self.check_resource_conflicts(resource)?; |
| } |
| |
| self.commands.push(Box::new(Cmd { copy_image_info })); |
| |
| for resource in resources { |
| self.add_resource(resource); |
| } |
| |
| Ok(()) |
| } |
| |
| /// Calls `vkCmdCopyBufferToImage` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn copy_buffer_to_image( |
| &mut self, |
| copy_buffer_to_image_info: CopyBufferToImageInfo, |
| ) -> Result<(), SyncCommandBufferBuilderError> { |
| struct Cmd { |
| copy_buffer_to_image_info: CopyBufferToImageInfo, |
| } |
| |
| impl Command for Cmd { |
| fn name(&self) -> &'static str { |
| "copy_buffer_to_image" |
| } |
| |
| unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { |
| out.copy_buffer_to_image(&self.copy_buffer_to_image_info); |
| } |
| } |
| |
| let &CopyBufferToImageInfo { |
| ref src_buffer, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = ©_buffer_to_image_info; |
| |
| let command_index = self.commands.len(); |
| let command_name = "copy_buffer_to_image"; |
| let resources: SmallVec<[_; 8]> = regions |
| .iter() |
| .flat_map(|region| { |
| let &BufferImageCopy { |
| buffer_offset, |
| buffer_row_length: _, |
| buffer_image_height: _, |
| ref image_subresource, |
| image_offset: _, |
| image_extent: _, |
| _ne: _, |
| } = region; |
| |
| [ |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Source, |
| secondary_use_ref: None, |
| }, |
| Resource::Buffer { |
| buffer: src_buffer.clone(), |
| range: buffer_offset |
| ..buffer_offset + region.buffer_copy_size(dst_image.format()), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_READ, |
| exclusive: false, |
| }, |
| }, |
| ), |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Destination, |
| secondary_use_ref: None, |
| }, |
| Resource::Image { |
| image: dst_image.clone(), |
| subresource_range: image_subresource.clone().into(), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_WRITE, |
| exclusive: true, |
| }, |
| start_layout: dst_image_layout, |
| end_layout: dst_image_layout, |
| }, |
| ), |
| ] |
| }) |
| .collect(); |
| |
| for resource in &resources { |
| self.check_resource_conflicts(resource)?; |
| } |
| |
| self.commands.push(Box::new(Cmd { |
| copy_buffer_to_image_info, |
| })); |
| |
| for resource in resources { |
| self.add_resource(resource); |
| } |
| |
| Ok(()) |
| } |
| |
| /// Calls `vkCmdCopyImageToBuffer` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn copy_image_to_buffer( |
| &mut self, |
| copy_image_to_buffer_info: CopyImageToBufferInfo, |
| ) -> Result<(), SyncCommandBufferBuilderError> { |
| struct Cmd { |
| copy_image_to_buffer_info: CopyImageToBufferInfo, |
| } |
| |
| impl Command for Cmd { |
| fn name(&self) -> &'static str { |
| "copy_image_to_buffer" |
| } |
| |
| unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { |
| out.copy_image_to_buffer(&self.copy_image_to_buffer_info); |
| } |
| } |
| |
| let &CopyImageToBufferInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_buffer, |
| ref regions, |
| _ne: _, |
| } = ©_image_to_buffer_info; |
| |
| let command_index = self.commands.len(); |
| let command_name = "copy_image_to_buffer"; |
| let resources: SmallVec<[_; 8]> = regions |
| .iter() |
| .flat_map(|region| { |
| let &BufferImageCopy { |
| buffer_offset, |
| buffer_row_length: _, |
| buffer_image_height: _, |
| ref image_subresource, |
| image_offset: _, |
| image_extent: _, |
| _ne: _, |
| } = region; |
| |
| [ |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Source, |
| secondary_use_ref: None, |
| }, |
| Resource::Image { |
| image: src_image.clone(), |
| subresource_range: image_subresource.clone().into(), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_READ, |
| exclusive: false, |
| }, |
| start_layout: src_image_layout, |
| end_layout: src_image_layout, |
| }, |
| ), |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Destination, |
| secondary_use_ref: None, |
| }, |
| Resource::Buffer { |
| buffer: dst_buffer.clone(), |
| range: buffer_offset |
| ..buffer_offset + region.buffer_copy_size(src_image.format()), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_WRITE, |
| exclusive: true, |
| }, |
| }, |
| ), |
| ] |
| }) |
| .collect(); |
| |
| for resource in &resources { |
| self.check_resource_conflicts(resource)?; |
| } |
| |
| self.commands.push(Box::new(Cmd { |
| copy_image_to_buffer_info, |
| })); |
| |
| for resource in resources { |
| self.add_resource(resource); |
| } |
| |
| Ok(()) |
| } |
| |
| /// Calls `vkCmdBlitImage` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn blit_image( |
| &mut self, |
| blit_image_info: BlitImageInfo, |
| ) -> Result<(), SyncCommandBufferBuilderError> { |
| struct Cmd { |
| blit_image_info: BlitImageInfo, |
| } |
| |
| impl Command for Cmd { |
| fn name(&self) -> &'static str { |
| "blit_image" |
| } |
| |
| unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { |
| out.blit_image(&self.blit_image_info); |
| } |
| } |
| |
| let &BlitImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| filter: _, |
| _ne: _, |
| } = &blit_image_info; |
| |
| let command_index = self.commands.len(); |
| let command_name = "blit_image"; |
| let resources: SmallVec<[_; 8]> = regions |
| .iter() |
| .flat_map(|region| { |
| let &ImageBlit { |
| ref src_subresource, |
| src_offsets: _, |
| ref dst_subresource, |
| dst_offsets: _, |
| _ne: _, |
| } = region; |
| |
| [ |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Source, |
| secondary_use_ref: None, |
| }, |
| Resource::Image { |
| image: src_image.clone(), |
| subresource_range: src_subresource.clone().into(), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_READ, |
| exclusive: false, |
| }, |
| start_layout: src_image_layout, |
| end_layout: src_image_layout, |
| }, |
| ), |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Destination, |
| secondary_use_ref: None, |
| }, |
| Resource::Image { |
| image: dst_image.clone(), |
| subresource_range: dst_subresource.clone().into(), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_WRITE, |
| exclusive: true, |
| }, |
| start_layout: dst_image_layout, |
| end_layout: dst_image_layout, |
| }, |
| ), |
| ] |
| }) |
| .collect(); |
| |
| for resource in &resources { |
| self.check_resource_conflicts(resource)?; |
| } |
| |
| self.commands.push(Box::new(Cmd { blit_image_info })); |
| |
| for resource in resources { |
| self.add_resource(resource); |
| } |
| |
| Ok(()) |
| } |
| |
| /// Calls `vkCmdResolveImage` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn resolve_image( |
| &mut self, |
| resolve_image_info: ResolveImageInfo, |
| ) -> Result<(), SyncCommandBufferBuilderError> { |
| struct Cmd { |
| resolve_image_info: ResolveImageInfo, |
| } |
| |
| impl Command for Cmd { |
| fn name(&self) -> &'static str { |
| "resolve_image" |
| } |
| |
| unsafe fn send(&self, out: &mut UnsafeCommandBufferBuilder) { |
| out.resolve_image(&self.resolve_image_info); |
| } |
| } |
| |
| let &ResolveImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = &resolve_image_info; |
| |
| let command_index = self.commands.len(); |
| let command_name = "resolve_image"; |
| let resources: SmallVec<[_; 8]> = regions |
| .iter() |
| .flat_map(|region| { |
| let &ImageResolve { |
| ref src_subresource, |
| src_offset: _, |
| ref dst_subresource, |
| dst_offset: _, |
| extent: _, |
| _ne: _, |
| } = region; |
| |
| [ |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Source, |
| secondary_use_ref: None, |
| }, |
| Resource::Image { |
| image: src_image.clone(), |
| subresource_range: src_subresource.clone().into(), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_READ, |
| exclusive: false, |
| }, |
| start_layout: src_image_layout, |
| end_layout: src_image_layout, |
| }, |
| ), |
| ( |
| ResourceUseRef { |
| command_index, |
| command_name, |
| resource_in_command: ResourceInCommand::Destination, |
| secondary_use_ref: None, |
| }, |
| Resource::Image { |
| image: dst_image.clone(), |
| subresource_range: dst_subresource.clone().into(), |
| memory: PipelineMemoryAccess { |
| stages: PipelineStages::ALL_TRANSFER, |
| access: AccessFlags::TRANSFER_WRITE, |
| exclusive: true, |
| }, |
| start_layout: dst_image_layout, |
| end_layout: dst_image_layout, |
| }, |
| ), |
| ] |
| }) |
| .collect(); |
| |
| for resource in &resources { |
| self.check_resource_conflicts(resource)?; |
| } |
| |
| self.commands.push(Box::new(Cmd { resolve_image_info })); |
| |
| for resource in resources { |
| self.add_resource(resource); |
| } |
| |
| Ok(()) |
| } |
| } |
| |
| impl UnsafeCommandBufferBuilder { |
| /// Calls `vkCmdCopyBuffer` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn copy_buffer(&mut self, copy_buffer_info: &CopyBufferInfo) { |
| let CopyBufferInfo { |
| src_buffer, |
| dst_buffer, |
| regions, |
| _ne: _, |
| } = copy_buffer_info; |
| |
| if regions.is_empty() { |
| return; |
| } |
| |
| let fns = self.device.fns(); |
| |
| if self.device.api_version() >= Version::V1_3 |
| || self.device.enabled_extensions().khr_copy_commands2 |
| { |
| let regions: SmallVec<[_; 8]> = regions |
| .iter() |
| .map(|region| { |
| let &BufferCopy { |
| src_offset, |
| dst_offset, |
| size, |
| _ne, |
| } = region; |
| |
| ash::vk::BufferCopy2 { |
| src_offset: src_offset + src_buffer.offset(), |
| dst_offset: dst_offset + dst_buffer.offset(), |
| size, |
| ..Default::default() |
| } |
| }) |
| .collect(); |
| |
| let copy_buffer_info = ash::vk::CopyBufferInfo2 { |
| src_buffer: src_buffer.buffer().handle(), |
| dst_buffer: dst_buffer.buffer().handle(), |
| region_count: regions.len() as u32, |
| p_regions: regions.as_ptr(), |
| ..Default::default() |
| }; |
| |
| if self.device.api_version() >= Version::V1_3 { |
| (fns.v1_3.cmd_copy_buffer2)(self.handle, ©_buffer_info); |
| } else { |
| (fns.khr_copy_commands2.cmd_copy_buffer2_khr)(self.handle, ©_buffer_info); |
| } |
| } else { |
| let regions: SmallVec<[_; 8]> = regions |
| .iter() |
| .map(|region| { |
| let &BufferCopy { |
| src_offset, |
| dst_offset, |
| size, |
| _ne, |
| } = region; |
| |
| ash::vk::BufferCopy { |
| src_offset: src_offset + src_buffer.offset(), |
| dst_offset: dst_offset + dst_buffer.offset(), |
| size, |
| } |
| }) |
| .collect(); |
| |
| (fns.v1_0.cmd_copy_buffer)( |
| self.handle, |
| src_buffer.buffer().handle(), |
| dst_buffer.buffer().handle(), |
| regions.len() as u32, |
| regions.as_ptr(), |
| ); |
| } |
| } |
| |
| /// Calls `vkCmdCopyImage` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn copy_image(&mut self, copy_image_info: &CopyImageInfo) { |
| let &CopyImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = copy_image_info; |
| |
| if regions.is_empty() { |
| return; |
| } |
| |
| let src_image_inner = src_image.inner(); |
| let dst_image_inner = dst_image.inner(); |
| |
| let fns = self.device.fns(); |
| |
| if self.device.api_version() >= Version::V1_3 |
| || self.device.enabled_extensions().khr_copy_commands2 |
| { |
| let regions: SmallVec<[_; 8]> = regions |
| .into_iter() |
| .map(|region| { |
| let &ImageCopy { |
| ref src_subresource, |
| src_offset, |
| ref dst_subresource, |
| dst_offset, |
| extent, |
| _ne: _, |
| } = region; |
| |
| let mut src_subresource = src_subresource.clone(); |
| src_subresource.array_layers.start += src_image_inner.first_layer; |
| src_subresource.array_layers.end += src_image_inner.first_layer; |
| src_subresource.mip_level += src_image_inner.first_mipmap_level; |
| |
| let mut dst_subresource = dst_subresource.clone(); |
| dst_subresource.array_layers.start += dst_image_inner.first_layer; |
| dst_subresource.array_layers.end += dst_image_inner.first_layer; |
| dst_subresource.mip_level += dst_image_inner.first_mipmap_level; |
| |
| ash::vk::ImageCopy2 { |
| src_subresource: src_subresource.into(), |
| src_offset: ash::vk::Offset3D { |
| x: src_offset[0] as i32, |
| y: src_offset[1] as i32, |
| z: src_offset[2] as i32, |
| }, |
| dst_subresource: dst_subresource.into(), |
| dst_offset: ash::vk::Offset3D { |
| x: dst_offset[0] as i32, |
| y: dst_offset[1] as i32, |
| z: dst_offset[2] as i32, |
| }, |
| extent: ash::vk::Extent3D { |
| width: extent[0], |
| height: extent[1], |
| depth: extent[2], |
| }, |
| ..Default::default() |
| } |
| }) |
| .collect(); |
| |
| let copy_image_info = ash::vk::CopyImageInfo2 { |
| src_image: src_image_inner.image.handle(), |
| src_image_layout: src_image_layout.into(), |
| dst_image: dst_image_inner.image.handle(), |
| dst_image_layout: dst_image_layout.into(), |
| region_count: regions.len() as u32, |
| p_regions: regions.as_ptr(), |
| ..Default::default() |
| }; |
| |
| if self.device.api_version() >= Version::V1_3 { |
| (fns.v1_3.cmd_copy_image2)(self.handle, ©_image_info); |
| } else { |
| (fns.khr_copy_commands2.cmd_copy_image2_khr)(self.handle, ©_image_info); |
| } |
| } else { |
| let regions: SmallVec<[_; 8]> = regions |
| .into_iter() |
| .map(|region| { |
| let &ImageCopy { |
| ref src_subresource, |
| src_offset, |
| ref dst_subresource, |
| dst_offset, |
| extent, |
| _ne: _, |
| } = region; |
| |
| let mut src_subresource = src_subresource.clone(); |
| src_subresource.array_layers.start += src_image_inner.first_layer; |
| src_subresource.array_layers.end += src_image_inner.first_layer; |
| src_subresource.mip_level += src_image_inner.first_mipmap_level; |
| |
| let mut dst_subresource = dst_subresource.clone(); |
| dst_subresource.array_layers.start += dst_image_inner.first_layer; |
| dst_subresource.array_layers.end += dst_image_inner.first_layer; |
| dst_subresource.mip_level += dst_image_inner.first_mipmap_level; |
| |
| ash::vk::ImageCopy { |
| src_subresource: src_subresource.into(), |
| src_offset: ash::vk::Offset3D { |
| x: src_offset[0] as i32, |
| y: src_offset[1] as i32, |
| z: src_offset[2] as i32, |
| }, |
| dst_subresource: dst_subresource.into(), |
| dst_offset: ash::vk::Offset3D { |
| x: dst_offset[0] as i32, |
| y: dst_offset[1] as i32, |
| z: dst_offset[2] as i32, |
| }, |
| extent: ash::vk::Extent3D { |
| width: extent[0], |
| height: extent[1], |
| depth: extent[2], |
| }, |
| } |
| }) |
| .collect(); |
| |
| (fns.v1_0.cmd_copy_image)( |
| self.handle, |
| src_image_inner.image.handle(), |
| src_image_layout.into(), |
| dst_image_inner.image.handle(), |
| dst_image_layout.into(), |
| regions.len() as u32, |
| regions.as_ptr(), |
| ); |
| } |
| } |
| |
| /// Calls `vkCmdCopyBufferToImage` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn copy_buffer_to_image( |
| &mut self, |
| copy_buffer_to_image_info: &CopyBufferToImageInfo, |
| ) { |
| let &CopyBufferToImageInfo { |
| ref src_buffer, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = copy_buffer_to_image_info; |
| |
| if regions.is_empty() { |
| return; |
| } |
| |
| let dst_image_inner = dst_image.inner(); |
| |
| let fns = self.device.fns(); |
| |
| if self.device.api_version() >= Version::V1_3 |
| || self.device.enabled_extensions().khr_copy_commands2 |
| { |
| let regions: SmallVec<[_; 8]> = regions |
| .iter() |
| .map(|region| { |
| let &BufferImageCopy { |
| buffer_offset, |
| buffer_row_length, |
| buffer_image_height, |
| ref image_subresource, |
| image_offset, |
| image_extent, |
| _ne: _, |
| } = region; |
| |
| let mut image_subresource = image_subresource.clone(); |
| image_subresource.array_layers.start += dst_image_inner.first_layer; |
| image_subresource.array_layers.end += dst_image_inner.first_layer; |
| image_subresource.mip_level += dst_image_inner.first_mipmap_level; |
| |
| ash::vk::BufferImageCopy2 { |
| buffer_offset: buffer_offset + src_buffer.offset(), |
| buffer_row_length, |
| buffer_image_height, |
| image_subresource: image_subresource.into(), |
| image_offset: ash::vk::Offset3D { |
| x: image_offset[0] as i32, |
| y: image_offset[1] as i32, |
| z: image_offset[2] as i32, |
| }, |
| image_extent: ash::vk::Extent3D { |
| width: image_extent[0], |
| height: image_extent[1], |
| depth: image_extent[2], |
| }, |
| ..Default::default() |
| } |
| }) |
| .collect(); |
| |
| let copy_buffer_to_image_info = ash::vk::CopyBufferToImageInfo2 { |
| src_buffer: src_buffer.buffer().handle(), |
| dst_image: dst_image_inner.image.handle(), |
| dst_image_layout: dst_image_layout.into(), |
| region_count: regions.len() as u32, |
| p_regions: regions.as_ptr(), |
| ..Default::default() |
| }; |
| |
| if self.device.api_version() >= Version::V1_3 { |
| (fns.v1_3.cmd_copy_buffer_to_image2)(self.handle, ©_buffer_to_image_info); |
| } else { |
| (fns.khr_copy_commands2.cmd_copy_buffer_to_image2_khr)( |
| self.handle, |
| ©_buffer_to_image_info, |
| ); |
| } |
| } else { |
| let regions: SmallVec<[_; 8]> = regions |
| .iter() |
| .map(|region| { |
| let &BufferImageCopy { |
| buffer_offset, |
| buffer_row_length, |
| buffer_image_height, |
| ref image_subresource, |
| image_offset, |
| image_extent, |
| _ne: _, |
| } = region; |
| |
| let mut image_subresource = image_subresource.clone(); |
| image_subresource.array_layers.start += dst_image_inner.first_layer; |
| image_subresource.array_layers.end += dst_image_inner.first_layer; |
| image_subresource.mip_level += dst_image_inner.first_mipmap_level; |
| |
| ash::vk::BufferImageCopy { |
| buffer_offset: buffer_offset + src_buffer.offset(), |
| buffer_row_length, |
| buffer_image_height, |
| image_subresource: image_subresource.into(), |
| image_offset: ash::vk::Offset3D { |
| x: image_offset[0] as i32, |
| y: image_offset[1] as i32, |
| z: image_offset[2] as i32, |
| }, |
| image_extent: ash::vk::Extent3D { |
| width: image_extent[0], |
| height: image_extent[1], |
| depth: image_extent[2], |
| }, |
| } |
| }) |
| .collect(); |
| |
| (fns.v1_0.cmd_copy_buffer_to_image)( |
| self.handle, |
| src_buffer.buffer().handle(), |
| dst_image_inner.image.handle(), |
| dst_image_layout.into(), |
| regions.len() as u32, |
| regions.as_ptr(), |
| ); |
| } |
| } |
| |
| /// Calls `vkCmdCopyImageToBuffer` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn copy_image_to_buffer( |
| &mut self, |
| copy_image_to_buffer_info: &CopyImageToBufferInfo, |
| ) { |
| let &CopyImageToBufferInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_buffer, |
| ref regions, |
| _ne: _, |
| } = copy_image_to_buffer_info; |
| |
| if regions.is_empty() { |
| return; |
| } |
| |
| let src_image_inner = src_image.inner(); |
| |
| let fns = self.device.fns(); |
| |
| if self.device.api_version() >= Version::V1_3 |
| || self.device.enabled_extensions().khr_copy_commands2 |
| { |
| let regions: SmallVec<[_; 8]> = regions |
| .iter() |
| .map(|region| { |
| let &BufferImageCopy { |
| buffer_offset, |
| buffer_row_length, |
| buffer_image_height, |
| ref image_subresource, |
| image_offset, |
| image_extent, |
| _ne: _, |
| } = region; |
| |
| let mut image_subresource = image_subresource.clone(); |
| image_subresource.array_layers.start += src_image_inner.first_layer; |
| image_subresource.array_layers.end += src_image_inner.first_layer; |
| image_subresource.mip_level += src_image_inner.first_mipmap_level; |
| |
| ash::vk::BufferImageCopy2 { |
| buffer_offset: buffer_offset + dst_buffer.offset(), |
| buffer_row_length, |
| buffer_image_height, |
| image_subresource: image_subresource.into(), |
| image_offset: ash::vk::Offset3D { |
| x: image_offset[0] as i32, |
| y: image_offset[1] as i32, |
| z: image_offset[2] as i32, |
| }, |
| image_extent: ash::vk::Extent3D { |
| width: image_extent[0], |
| height: image_extent[1], |
| depth: image_extent[2], |
| }, |
| ..Default::default() |
| } |
| }) |
| .collect(); |
| |
| let copy_image_to_buffer_info = ash::vk::CopyImageToBufferInfo2 { |
| src_image: src_image_inner.image.handle(), |
| src_image_layout: src_image_layout.into(), |
| dst_buffer: dst_buffer.buffer().handle(), |
| region_count: regions.len() as u32, |
| p_regions: regions.as_ptr(), |
| ..Default::default() |
| }; |
| |
| if self.device.api_version() >= Version::V1_3 { |
| (fns.v1_3.cmd_copy_image_to_buffer2)(self.handle, ©_image_to_buffer_info); |
| } else { |
| (fns.khr_copy_commands2.cmd_copy_image_to_buffer2_khr)( |
| self.handle, |
| ©_image_to_buffer_info, |
| ); |
| } |
| } else { |
| let regions: SmallVec<[_; 8]> = regions |
| .iter() |
| .map(|region| { |
| let &BufferImageCopy { |
| buffer_offset, |
| buffer_row_length, |
| buffer_image_height, |
| ref image_subresource, |
| image_offset, |
| image_extent, |
| _ne: _, |
| } = region; |
| let mut image_subresource = image_subresource.clone(); |
| image_subresource.array_layers.start += src_image_inner.first_layer; |
| image_subresource.array_layers.end += src_image_inner.first_layer; |
| image_subresource.mip_level += src_image_inner.first_mipmap_level; |
| |
| ash::vk::BufferImageCopy { |
| buffer_offset: buffer_offset + dst_buffer.offset(), |
| buffer_row_length, |
| buffer_image_height, |
| image_subresource: image_subresource.into(), |
| image_offset: ash::vk::Offset3D { |
| x: image_offset[0] as i32, |
| y: image_offset[1] as i32, |
| z: image_offset[2] as i32, |
| }, |
| image_extent: ash::vk::Extent3D { |
| width: image_extent[0], |
| height: image_extent[1], |
| depth: image_extent[2], |
| }, |
| } |
| }) |
| .collect(); |
| |
| (fns.v1_0.cmd_copy_image_to_buffer)( |
| self.handle, |
| src_image_inner.image.handle(), |
| src_image_layout.into(), |
| dst_buffer.buffer().handle(), |
| regions.len() as u32, |
| regions.as_ptr(), |
| ); |
| } |
| } |
| |
| /// Calls `vkCmdBlitImage` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn blit_image(&mut self, blit_image_info: &BlitImageInfo) { |
| let &BlitImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| filter, |
| _ne, |
| } = blit_image_info; |
| |
| if regions.is_empty() { |
| return; |
| } |
| |
| let src_image_inner = src_image.inner(); |
| let dst_image_inner = dst_image.inner(); |
| |
| let fns = self.device.fns(); |
| |
| if self.device.api_version() >= Version::V1_3 |
| || self.device.enabled_extensions().khr_copy_commands2 |
| { |
| let regions: SmallVec<[_; 8]> = regions |
| .into_iter() |
| .map(|region| { |
| let &ImageBlit { |
| ref src_subresource, |
| src_offsets, |
| ref dst_subresource, |
| dst_offsets, |
| _ne: _, |
| } = region; |
| |
| let mut src_subresource = src_subresource.clone(); |
| src_subresource.array_layers.start += src_image_inner.first_layer; |
| src_subresource.array_layers.end += src_image_inner.first_layer; |
| src_subresource.mip_level += src_image_inner.first_mipmap_level; |
| |
| let mut dst_subresource = dst_subresource.clone(); |
| dst_subresource.array_layers.start += dst_image_inner.first_layer; |
| dst_subresource.array_layers.end += dst_image_inner.first_layer; |
| dst_subresource.mip_level += dst_image_inner.first_mipmap_level; |
| |
| ash::vk::ImageBlit2 { |
| src_subresource: src_subresource.into(), |
| src_offsets: [ |
| ash::vk::Offset3D { |
| x: src_offsets[0][0] as i32, |
| y: src_offsets[0][1] as i32, |
| z: src_offsets[0][2] as i32, |
| }, |
| ash::vk::Offset3D { |
| x: src_offsets[1][0] as i32, |
| y: src_offsets[1][1] as i32, |
| z: src_offsets[1][2] as i32, |
| }, |
| ], |
| dst_subresource: dst_subresource.into(), |
| dst_offsets: [ |
| ash::vk::Offset3D { |
| x: dst_offsets[0][0] as i32, |
| y: dst_offsets[0][1] as i32, |
| z: dst_offsets[0][2] as i32, |
| }, |
| ash::vk::Offset3D { |
| x: dst_offsets[1][0] as i32, |
| y: dst_offsets[1][1] as i32, |
| z: dst_offsets[1][2] as i32, |
| }, |
| ], |
| ..Default::default() |
| } |
| }) |
| .collect(); |
| |
| let blit_image_info = ash::vk::BlitImageInfo2 { |
| src_image: src_image_inner.image.handle(), |
| src_image_layout: src_image_layout.into(), |
| dst_image: dst_image_inner.image.handle(), |
| dst_image_layout: dst_image_layout.into(), |
| region_count: regions.len() as u32, |
| p_regions: regions.as_ptr(), |
| filter: filter.into(), |
| ..Default::default() |
| }; |
| |
| if self.device.api_version() >= Version::V1_3 { |
| (fns.v1_3.cmd_blit_image2)(self.handle, &blit_image_info); |
| } else { |
| (fns.khr_copy_commands2.cmd_blit_image2_khr)(self.handle, &blit_image_info); |
| } |
| } else { |
| let regions: SmallVec<[_; 8]> = regions |
| .into_iter() |
| .map(|region| { |
| let &ImageBlit { |
| ref src_subresource, |
| src_offsets, |
| ref dst_subresource, |
| dst_offsets, |
| _ne: _, |
| } = region; |
| |
| let mut src_subresource = src_subresource.clone(); |
| src_subresource.array_layers.start += src_image_inner.first_layer; |
| src_subresource.array_layers.end += src_image_inner.first_layer; |
| src_subresource.mip_level += src_image_inner.first_mipmap_level; |
| |
| let mut dst_subresource = dst_subresource.clone(); |
| dst_subresource.array_layers.start += dst_image_inner.first_layer; |
| dst_subresource.array_layers.end += dst_image_inner.first_layer; |
| dst_subresource.mip_level += dst_image_inner.first_mipmap_level; |
| |
| ash::vk::ImageBlit { |
| src_subresource: src_subresource.into(), |
| src_offsets: [ |
| ash::vk::Offset3D { |
| x: src_offsets[0][0] as i32, |
| y: src_offsets[0][1] as i32, |
| z: src_offsets[0][2] as i32, |
| }, |
| ash::vk::Offset3D { |
| x: src_offsets[1][0] as i32, |
| y: src_offsets[1][1] as i32, |
| z: src_offsets[1][2] as i32, |
| }, |
| ], |
| dst_subresource: dst_subresource.into(), |
| dst_offsets: [ |
| ash::vk::Offset3D { |
| x: dst_offsets[0][0] as i32, |
| y: dst_offsets[0][1] as i32, |
| z: dst_offsets[0][2] as i32, |
| }, |
| ash::vk::Offset3D { |
| x: dst_offsets[1][0] as i32, |
| y: dst_offsets[1][1] as i32, |
| z: dst_offsets[1][2] as i32, |
| }, |
| ], |
| } |
| }) |
| .collect(); |
| |
| (fns.v1_0.cmd_blit_image)( |
| self.handle, |
| src_image_inner.image.handle(), |
| src_image_layout.into(), |
| dst_image_inner.image.handle(), |
| dst_image_layout.into(), |
| regions.len() as u32, |
| regions.as_ptr(), |
| filter.into(), |
| ); |
| } |
| } |
| |
| /// Calls `vkCmdResolveImage` on the builder. |
| /// |
| /// Does nothing if the list of regions is empty, as it would be a no-op and isn't a valid |
| /// usage of the command anyway. |
| #[inline] |
| pub unsafe fn resolve_image(&mut self, resolve_image_info: &ResolveImageInfo) { |
| let &ResolveImageInfo { |
| ref src_image, |
| src_image_layout, |
| ref dst_image, |
| dst_image_layout, |
| ref regions, |
| _ne: _, |
| } = resolve_image_info; |
| |
| if regions.is_empty() { |
| return; |
| } |
| |
| let src_image_inner = src_image.inner(); |
| let dst_image_inner = dst_image.inner(); |
| |
| let fns = self.device.fns(); |
| |
| if self.device.api_version() >= Version::V1_3 |
| || self.device.enabled_extensions().khr_copy_commands2 |
| { |
| let regions: SmallVec<[_; 8]> = regions |
| .into_iter() |
| .map(|region| { |
| let &ImageResolve { |
| ref src_subresource, |
| src_offset, |
| ref dst_subresource, |
| dst_offset, |
| extent, |
| _ne: _, |
| } = region; |
| |
| let mut src_subresource = src_subresource.clone(); |
| src_subresource.array_layers.start += src_image_inner.first_layer; |
| src_subresource.array_layers.end += src_image_inner.first_layer; |
| src_subresource.mip_level += src_image_inner.first_mipmap_level; |
| |
| let mut dst_subresource = dst_subresource.clone(); |
| dst_subresource.array_layers.start += dst_image_inner.first_layer; |
| dst_subresource.array_layers.end += dst_image_inner.first_layer; |
| dst_subresource.mip_level += dst_image_inner.first_mipmap_level; |
| |
| ash::vk::ImageResolve2 { |
| src_subresource: src_subresource.into(), |
| src_offset: ash::vk::Offset3D { |
| x: src_offset[0] as i32, |
| y: src_offset[1] as i32, |
| z: src_offset[2] as i32, |
| }, |
| dst_subresource: dst_subresource.into(), |
| dst_offset: ash::vk::Offset3D { |
| x: dst_offset[0] as i32, |
| y: dst_offset[1] as i32, |
| z: dst_offset[2] as i32, |
| }, |
| extent: ash::vk::Extent3D { |
| width: extent[0], |
| height: extent[1], |
| depth: extent[2], |
| }, |
| ..Default::default() |
| } |
| }) |
| .collect(); |
| |
| let resolve_image_info = ash::vk::ResolveImageInfo2 { |
| src_image: src_image_inner.image.handle(), |
| src_image_layout: src_image_layout.into(), |
| dst_image: dst_image_inner.image.handle(), |
| dst_image_layout: dst_image_layout.into(), |
| region_count: regions.len() as u32, |
| p_regions: regions.as_ptr(), |
| ..Default::default() |
| }; |
| |
| if self.device.api_version() >= Version::V1_3 { |
| (fns.v1_3.cmd_resolve_image2)(self.handle, &resolve_image_info); |
| } else { |
| (fns.khr_copy_commands2.cmd_resolve_image2_khr)(self.handle, &resolve_image_info); |
| } |
| } else { |
| let regions: SmallVec<[_; 8]> = regions |
| .into_iter() |
| .map(|region| { |
| let &ImageResolve { |
| ref src_subresource, |
| src_offset, |
| ref dst_subresource, |
| dst_offset, |
| extent, |
| _ne: _, |
| } = region; |
| |
| let mut src_subresource = src_subresource.clone(); |
| src_subresource.array_layers.start += src_image_inner.first_layer; |
| src_subresource.array_layers.end += src_image_inner.first_layer; |
| src_subresource.mip_level += src_image_inner.first_mipmap_level; |
| |
| let mut dst_subresource = dst_subresource.clone(); |
| dst_subresource.array_layers.start += dst_image_inner.first_layer; |
| dst_subresource.array_layers.end += dst_image_inner.first_layer; |
| dst_subresource.mip_level += dst_image_inner.first_mipmap_level; |
| |
| ash::vk::ImageResolve { |
| src_subresource: src_subresource.into(), |
| src_offset: ash::vk::Offset3D { |
| x: src_offset[0] as i32, |
| y: src_offset[1] as i32, |
| z: src_offset[2] as i32, |
| }, |
| dst_subresource: dst_subresource.into(), |
| dst_offset: ash::vk::Offset3D { |
| x: dst_offset[0] as i32, |
| y: dst_offset[1] as i32, |
| z: dst_offset[2] as i32, |
| }, |
| extent: ash::vk::Extent3D { |
| width: extent[0], |
| height: extent[1], |
| depth: extent[2], |
| }, |
| } |
| }) |
| .collect(); |
| |
| (fns.v1_0.cmd_resolve_image)( |
| self.handle, |
| src_image_inner.image.handle(), |
| src_image_layout.into(), |
| dst_image_inner.image.handle(), |
| dst_image_layout.into(), |
| regions.len() as u32, |
| regions.as_ptr(), |
| ); |
| } |
| } |
| } |
| |
| /// Parameters to copy data from a buffer to another buffer. |
| /// |
| /// The fields of `regions` represent bytes. |
| #[derive(Clone, Debug)] |
| pub struct CopyBufferInfo { |
| /// The buffer to copy from. |
| /// |
| /// There is no default value. |
| pub src_buffer: Subbuffer<[u8]>, |
| |
| /// The buffer to copy to. |
| /// |
| /// There is no default value. |
| pub dst_buffer: Subbuffer<[u8]>, |
| |
| /// The regions of both buffers to copy between, specified in bytes. |
| /// |
| /// The default value is a single region, with zero offsets and a `size` equal to the smallest |
| /// of the two buffers. |
| pub regions: SmallVec<[BufferCopy; 1]>, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl CopyBufferInfo { |
| /// Returns a `CopyBufferInfo` with the specified `src_buffer` and `dst_buffer`. |
| #[inline] |
| pub fn buffers(src_buffer: Subbuffer<impl ?Sized>, dst_buffer: Subbuffer<impl ?Sized>) -> Self { |
| let region = BufferCopy { |
| src_offset: 0, |
| dst_offset: 0, |
| size: min(src_buffer.size(), dst_buffer.size()), |
| ..Default::default() |
| }; |
| |
| Self { |
| src_buffer: src_buffer.into_bytes(), |
| dst_buffer: dst_buffer.into_bytes(), |
| regions: smallvec![region], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// Parameters to copy data from a buffer to another buffer, with type information. |
| /// |
| /// The fields of `regions` represent elements of `T`. |
| #[derive(Clone, Debug)] |
| pub struct CopyBufferInfoTyped<T> { |
| /// The buffer to copy from. |
| /// |
| /// There is no default value. |
| pub src_buffer: Subbuffer<[T]>, |
| |
| /// The buffer to copy to. |
| /// |
| /// There is no default value. |
| pub dst_buffer: Subbuffer<[T]>, |
| |
| /// The regions of both buffers to copy between, specified in elements of `T`. |
| /// |
| /// The default value is a single region, with zero offsets and a `size` equal to the smallest |
| /// of the two buffers. |
| pub regions: SmallVec<[BufferCopy; 1]>, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl<T> CopyBufferInfoTyped<T> { |
| /// Returns a `CopyBufferInfoTyped` with the specified `src_buffer` and `dst_buffer`. |
| pub fn buffers(src_buffer: Subbuffer<[T]>, dst_buffer: Subbuffer<[T]>) -> Self { |
| let region = BufferCopy { |
| size: min(src_buffer.len(), dst_buffer.len()), |
| ..Default::default() |
| }; |
| |
| Self { |
| src_buffer, |
| dst_buffer, |
| regions: smallvec![region], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| impl<T> From<CopyBufferInfoTyped<T>> for CopyBufferInfo { |
| fn from(typed: CopyBufferInfoTyped<T>) -> Self { |
| let CopyBufferInfoTyped { |
| src_buffer, |
| dst_buffer, |
| mut regions, |
| _ne: _, |
| } = typed; |
| |
| for region in &mut regions { |
| region.src_offset *= size_of::<T>() as DeviceSize; |
| region.dst_offset *= size_of::<T>() as DeviceSize; |
| region.size *= size_of::<T>() as DeviceSize; |
| } |
| |
| Self { |
| src_buffer: src_buffer.as_bytes().clone(), |
| dst_buffer: dst_buffer.as_bytes().clone(), |
| regions, |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// A region of data to copy between buffers. |
| #[derive(Clone, Debug)] |
| pub struct BufferCopy { |
| /// The offset in bytes or elements from the start of `src_buffer` that copying will |
| /// start from. |
| /// |
| /// The default value is `0`. |
| pub src_offset: DeviceSize, |
| |
| /// The offset in bytes or elements from the start of `dst_buffer` that copying will |
| /// start from. |
| /// |
| /// The default value is `0`. |
| pub dst_offset: DeviceSize, |
| |
| /// The number of bytes or elements to copy. |
| /// |
| /// The default value is `0`, which must be overridden. |
| pub size: DeviceSize, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl Default for BufferCopy { |
| #[inline] |
| fn default() -> Self { |
| Self { |
| src_offset: 0, |
| dst_offset: 0, |
| size: 0, |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// Parameters to copy data from an image to another image. |
| #[derive(Clone, Debug)] |
| pub struct CopyImageInfo { |
| /// The image to copy from. |
| /// |
| /// There is no default value. |
| pub src_image: Arc<dyn ImageAccess>, |
| |
| /// The layout used for `src_image` during the copy operation. |
| /// |
| /// The following layouts are allowed: |
| /// - [`ImageLayout::TransferSrcOptimal`] |
| /// - [`ImageLayout::General`] |
| /// |
| /// The default value is [`ImageLayout::TransferSrcOptimal`]. |
| pub src_image_layout: ImageLayout, |
| |
| /// The image to copy to. |
| /// |
| /// There is no default value. |
| pub dst_image: Arc<dyn ImageAccess>, |
| |
| /// The layout used for `dst_image` during the copy operation. |
| /// |
| /// The following layouts are allowed: |
| /// - [`ImageLayout::TransferDstOptimal`] |
| /// - [`ImageLayout::General`] |
| /// |
| /// The default value is [`ImageLayout::TransferDstOptimal`]. |
| pub dst_image_layout: ImageLayout, |
| |
| /// The regions of both images to copy between. |
| /// |
| /// The default value is a single region, covering the first mip level, and the smallest of the |
| /// array layers and extent of the two images. All aspects of each image are selected, or |
| /// `plane0` if the image is multi-planar. |
| pub regions: SmallVec<[ImageCopy; 1]>, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl CopyImageInfo { |
| /// Returns a `CopyImageInfo` with the specified `src_image` and `dst_image`. |
| #[inline] |
| pub fn images(src_image: Arc<dyn ImageAccess>, dst_image: Arc<dyn ImageAccess>) -> Self { |
| let min_array_layers = src_image |
| .dimensions() |
| .array_layers() |
| .min(dst_image.dimensions().array_layers()); |
| let region = ImageCopy { |
| src_subresource: ImageSubresourceLayers { |
| array_layers: 0..min_array_layers, |
| ..src_image.subresource_layers() |
| }, |
| dst_subresource: ImageSubresourceLayers { |
| array_layers: 0..min_array_layers, |
| ..dst_image.subresource_layers() |
| }, |
| extent: { |
| let src_extent = src_image.dimensions().width_height_depth(); |
| let dst_extent = dst_image.dimensions().width_height_depth(); |
| |
| [ |
| src_extent[0].min(dst_extent[0]), |
| src_extent[1].min(dst_extent[1]), |
| src_extent[2].min(dst_extent[2]), |
| ] |
| }, |
| ..Default::default() |
| }; |
| |
| Self { |
| src_image, |
| src_image_layout: ImageLayout::TransferSrcOptimal, |
| dst_image, |
| dst_image_layout: ImageLayout::TransferDstOptimal, |
| regions: smallvec![region], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// A region of data to copy between images. |
| #[derive(Clone, Debug)] |
| pub struct ImageCopy { |
| /// The subresource of `src_image` to copy from. |
| /// |
| /// The default value is empty, which must be overridden. |
| pub src_subresource: ImageSubresourceLayers, |
| |
| /// The offset from the zero coordinate of `src_image` that copying will start from. |
| /// |
| /// The default value is `[0; 3]`. |
| pub src_offset: [u32; 3], |
| |
| /// The subresource of `dst_image` to copy to. |
| /// |
| /// The default value is empty, which must be overridden. |
| pub dst_subresource: ImageSubresourceLayers, |
| |
| /// The offset from the zero coordinate of `dst_image` that copying will start from. |
| /// |
| /// The default value is `[0; 3]`. |
| pub dst_offset: [u32; 3], |
| |
| /// The extent of texels to copy. |
| /// |
| /// The default value is `[0; 3]`, which must be overridden. |
| pub extent: [u32; 3], |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl Default for ImageCopy { |
| #[inline] |
| fn default() -> Self { |
| Self { |
| src_subresource: ImageSubresourceLayers { |
| aspects: ImageAspects::empty(), |
| mip_level: 0, |
| array_layers: 0..0, |
| }, |
| src_offset: [0; 3], |
| dst_subresource: ImageSubresourceLayers { |
| aspects: ImageAspects::empty(), |
| mip_level: 0, |
| array_layers: 0..0, |
| }, |
| dst_offset: [0; 3], |
| extent: [0; 3], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// Parameters to copy data from a buffer to an image. |
| #[derive(Clone, Debug)] |
| pub struct CopyBufferToImageInfo { |
| /// The buffer to copy from. |
| /// |
| /// There is no default value. |
| pub src_buffer: Subbuffer<[u8]>, |
| |
| /// The image to copy to. |
| /// |
| /// There is no default value. |
| pub dst_image: Arc<dyn ImageAccess>, |
| |
| /// The layout used for `dst_image` during the copy operation. |
| /// |
| /// The following layouts are allowed: |
| /// - [`ImageLayout::TransferDstOptimal`] |
| /// - [`ImageLayout::General`] |
| /// |
| /// The default value is [`ImageLayout::TransferDstOptimal`]. |
| pub dst_image_layout: ImageLayout, |
| |
| /// The regions of the buffer and image to copy between. |
| /// |
| /// The default value is a single region, covering all of the buffer and the first mip level of |
| /// the image. All aspects of the image are selected, or `plane0` if the image is multi-planar. |
| pub regions: SmallVec<[BufferImageCopy; 1]>, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl CopyBufferToImageInfo { |
| /// Returns a `CopyBufferToImageInfo` with the specified `src_buffer` and |
| /// `dst_image`. |
| #[inline] |
| pub fn buffer_image( |
| src_buffer: Subbuffer<impl ?Sized>, |
| dst_image: Arc<dyn ImageAccess>, |
| ) -> Self { |
| let region = BufferImageCopy { |
| image_subresource: dst_image.subresource_layers(), |
| image_extent: dst_image.dimensions().width_height_depth(), |
| ..Default::default() |
| }; |
| |
| Self { |
| src_buffer: src_buffer.into_bytes(), |
| dst_image, |
| dst_image_layout: ImageLayout::TransferDstOptimal, |
| regions: smallvec![region], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// Parameters to copy data from an image to a buffer. |
| #[derive(Clone, Debug)] |
| pub struct CopyImageToBufferInfo { |
| /// The image to copy from. |
| /// |
| /// There is no default value. |
| pub src_image: Arc<dyn ImageAccess>, |
| |
| /// The layout used for `src_image` during the copy operation. |
| /// |
| /// The following layouts are allowed: |
| /// - [`ImageLayout::TransferSrcOptimal`] |
| /// - [`ImageLayout::General`] |
| /// |
| /// The default value is [`ImageLayout::TransferSrcOptimal`]. |
| pub src_image_layout: ImageLayout, |
| |
| /// The buffer to copy to. |
| /// |
| /// There is no default value. |
| pub dst_buffer: Subbuffer<[u8]>, |
| |
| /// The regions of the image and buffer to copy between. |
| /// |
| /// The default value is a single region, covering all of the buffer and the first mip level of |
| /// the image. All aspects of the image are selected, or `plane0` if the image is multi-planar. |
| pub regions: SmallVec<[BufferImageCopy; 1]>, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl CopyImageToBufferInfo { |
| /// Returns a `CopyImageToBufferInfo` with the specified `src_image` and |
| /// `dst_buffer`. |
| #[inline] |
| pub fn image_buffer( |
| src_image: Arc<dyn ImageAccess>, |
| dst_buffer: Subbuffer<impl ?Sized>, |
| ) -> Self { |
| let region = BufferImageCopy { |
| image_subresource: src_image.subresource_layers(), |
| image_extent: src_image.dimensions().width_height_depth(), |
| ..Default::default() |
| }; |
| |
| Self { |
| src_image, |
| src_image_layout: ImageLayout::TransferSrcOptimal, |
| dst_buffer: dst_buffer.into_bytes(), |
| regions: smallvec![region], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// A region of data to copy between a buffer and an image. |
| #[derive(Clone, Debug)] |
| pub struct BufferImageCopy { |
| /// The offset in bytes from the start of the buffer that copying will start from. |
| /// |
| /// The default value is `0`. |
| pub buffer_offset: DeviceSize, |
| |
| /// The number of texels between successive rows of image data in the buffer. |
| /// |
| /// If set to `0`, the width of the image is used. |
| /// |
| /// The default value is `0`. |
| pub buffer_row_length: u32, |
| |
| /// The number of rows between successive depth slices of image data in the buffer. |
| /// |
| /// If set to `0`, the height of the image is used. |
| /// |
| /// The default value is `0`. |
| pub buffer_image_height: u32, |
| |
| /// The subresource of the image to copy from/to. |
| /// |
| /// The default value is empty, which must be overridden. |
| pub image_subresource: ImageSubresourceLayers, |
| |
| /// The offset from the zero coordinate of the image that copying will start from. |
| /// |
| /// The default value is `[0; 3]`. |
| pub image_offset: [u32; 3], |
| |
| /// The extent of texels in the image to copy. |
| /// |
| /// The default value is `[0; 3]`, which must be overridden. |
| pub image_extent: [u32; 3], |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl Default for BufferImageCopy { |
| #[inline] |
| fn default() -> Self { |
| Self { |
| buffer_offset: 0, |
| buffer_row_length: 0, |
| buffer_image_height: 0, |
| image_subresource: ImageSubresourceLayers { |
| aspects: ImageAspects::empty(), |
| mip_level: 0, |
| array_layers: 0..0, |
| }, |
| image_offset: [0; 3], |
| image_extent: [0; 3], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| impl BufferImageCopy { |
| // Following |
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/html/chap20.html#copies-buffers-images-addressing |
| pub(crate) fn buffer_copy_size(&self, format: Format) -> DeviceSize { |
| let &BufferImageCopy { |
| buffer_offset: _, |
| mut buffer_row_length, |
| mut buffer_image_height, |
| ref image_subresource, |
| image_offset: _, |
| mut image_extent, |
| _ne: _, |
| } = self; |
| |
| if buffer_row_length == 0 { |
| buffer_row_length = image_extent[0]; |
| } |
| |
| if buffer_image_height == 0 { |
| buffer_image_height = image_extent[1]; |
| } |
| |
| // Scale down from texels to texel blocks, rounding up if needed. |
| let block_extent = format.block_extent(); |
| buffer_row_length = (buffer_row_length + block_extent[0] - 1) / block_extent[0]; |
| buffer_image_height = (buffer_image_height + block_extent[1] - 1) / block_extent[1]; |
| |
| for i in 0..3 { |
| image_extent[i] = (image_extent[i] + block_extent[i] - 1) / block_extent[i]; |
| } |
| |
| // Only one of these is greater than 1, take the greater number. |
| image_extent[2] = max( |
| image_extent[2], |
| image_subresource.array_layers.end - image_subresource.array_layers.start, |
| ); |
| |
| let blocks_to_last_slice = (image_extent[2] as DeviceSize - 1) |
| * buffer_image_height as DeviceSize |
| * buffer_row_length as DeviceSize; |
| let blocks_to_last_row = |
| (image_extent[1] as DeviceSize - 1) * buffer_row_length as DeviceSize; |
| let num_blocks = blocks_to_last_slice + blocks_to_last_row + image_extent[0] as DeviceSize; |
| |
| // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBufferImageCopy.html#_description |
| let block_size = if image_subresource.aspects.intersects(ImageAspects::STENCIL) { |
| 1 |
| } else if image_subresource.aspects.intersects(ImageAspects::DEPTH) { |
| match format { |
| Format::D16_UNORM | Format::D16_UNORM_S8_UINT => 2, |
| Format::D32_SFLOAT |
| | Format::D32_SFLOAT_S8_UINT |
| | Format::X8_D24_UNORM_PACK32 |
| | Format::D24_UNORM_S8_UINT => 4, |
| _ => unreachable!(), |
| } |
| } else { |
| format.block_size().unwrap() |
| }; |
| |
| num_blocks * block_size |
| } |
| } |
| |
| /// Parameters to blit image data. |
| #[derive(Clone, Debug)] |
| pub struct BlitImageInfo { |
| /// The image to blit from. |
| /// |
| /// There is no default value. |
| pub src_image: Arc<dyn ImageAccess>, |
| |
| /// The layout used for `src_image` during the blit operation. |
| /// |
| /// The following layouts are allowed: |
| /// - [`ImageLayout::TransferSrcOptimal`] |
| /// - [`ImageLayout::General`] |
| /// |
| /// The default value is [`ImageLayout::TransferSrcOptimal`]. |
| pub src_image_layout: ImageLayout, |
| |
| /// The image to blit to. |
| /// |
| /// There is no default value. |
| pub dst_image: Arc<dyn ImageAccess>, |
| |
| /// The layout used for `dst_image` during the blit operation. |
| /// |
| /// The following layouts are allowed: |
| /// - [`ImageLayout::TransferDstOptimal`] |
| /// - [`ImageLayout::General`] |
| /// |
| /// The default value is [`ImageLayout::TransferDstOptimal`]. |
| pub dst_image_layout: ImageLayout, |
| |
| /// The regions of both images to blit between. |
| /// |
| /// The default value is a single region, covering the first mip level, and the smallest of the |
| /// array layers of the two images. The whole extent of each image is covered, scaling if |
| /// necessary. All aspects of each image are selected, or `plane0` if the image is multi-planar. |
| pub regions: SmallVec<[ImageBlit; 1]>, |
| |
| /// The filter to use for sampling `src_image` when the `src_extent` and |
| /// `dst_extent` of a region are not the same size. |
| /// |
| /// The default value is [`Filter::Nearest`]. |
| pub filter: Filter, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl BlitImageInfo { |
| /// Returns a `BlitImageInfo` with the specified `src_image` and `dst_image`. |
| #[inline] |
| pub fn images(src_image: Arc<dyn ImageAccess>, dst_image: Arc<dyn ImageAccess>) -> Self { |
| let min_array_layers = src_image |
| .dimensions() |
| .array_layers() |
| .min(dst_image.dimensions().array_layers()); |
| let region = ImageBlit { |
| src_subresource: ImageSubresourceLayers { |
| array_layers: 0..min_array_layers, |
| ..src_image.subresource_layers() |
| }, |
| src_offsets: [[0; 3], src_image.dimensions().width_height_depth()], |
| dst_subresource: ImageSubresourceLayers { |
| array_layers: 0..min_array_layers, |
| ..dst_image.subresource_layers() |
| }, |
| dst_offsets: [[0; 3], dst_image.dimensions().width_height_depth()], |
| ..Default::default() |
| }; |
| |
| Self { |
| src_image, |
| src_image_layout: ImageLayout::TransferSrcOptimal, |
| dst_image, |
| dst_image_layout: ImageLayout::TransferDstOptimal, |
| regions: smallvec![region], |
| filter: Filter::Nearest, |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// A region of data to blit between images. |
| #[derive(Clone, Debug)] |
| pub struct ImageBlit { |
| /// The subresource of `src_image` to blit from. |
| /// |
| /// The default value is empty, which must be overridden. |
| pub src_subresource: ImageSubresourceLayers, |
| |
| /// The offsets from the zero coordinate of `src_image`, defining two corners of the region |
| /// to blit from. |
| /// If the ordering of the two offsets differs between source and destination, the image will |
| /// be flipped. |
| /// |
| /// The default value is `[[0; 3]; 2]`, which must be overridden. |
| pub src_offsets: [[u32; 3]; 2], |
| |
| /// The subresource of `dst_image` to blit to. |
| /// |
| /// The default value is empty, which must be overridden. |
| pub dst_subresource: ImageSubresourceLayers, |
| |
| /// The offset from the zero coordinate of `dst_image` defining two corners of the |
| /// region to blit to. |
| /// If the ordering of the two offsets differs between source and destination, the image will |
| /// be flipped. |
| /// |
| /// The default value is `[[0; 3]; 2]`, which must be overridden. |
| pub dst_offsets: [[u32; 3]; 2], |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl Default for ImageBlit { |
| #[inline] |
| fn default() -> Self { |
| Self { |
| src_subresource: ImageSubresourceLayers { |
| aspects: ImageAspects::empty(), |
| mip_level: 0, |
| array_layers: 0..0, |
| }, |
| src_offsets: [[0; 3]; 2], |
| dst_subresource: ImageSubresourceLayers { |
| aspects: ImageAspects::empty(), |
| mip_level: 0, |
| array_layers: 0..0, |
| }, |
| dst_offsets: [[0; 3]; 2], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// Parameters to resolve image data. |
| #[derive(Clone, Debug)] |
| pub struct ResolveImageInfo { |
| /// The multisampled image to resolve from. |
| /// |
| /// There is no default value. |
| pub src_image: Arc<dyn ImageAccess>, |
| |
| /// The layout used for `src_image` during the resolve operation. |
| /// |
| /// The following layouts are allowed: |
| /// - [`ImageLayout::TransferSrcOptimal`] |
| /// - [`ImageLayout::General`] |
| /// |
| /// The default value is [`ImageLayout::TransferSrcOptimal`]. |
| pub src_image_layout: ImageLayout, |
| |
| /// The non-multisampled image to resolve into. |
| /// |
| /// There is no default value. |
| pub dst_image: Arc<dyn ImageAccess>, |
| |
| /// The layout used for `dst_image` during the resolve operation. |
| /// |
| /// The following layouts are allowed: |
| /// - [`ImageLayout::TransferDstOptimal`] |
| /// - [`ImageLayout::General`] |
| /// |
| /// The default value is [`ImageLayout::TransferDstOptimal`]. |
| pub dst_image_layout: ImageLayout, |
| |
| /// The regions of both images to resolve between. |
| /// |
| /// The default value is a single region, covering the first mip level, and the smallest of the |
| /// array layers and extent of the two images. All aspects of each image are selected, or |
| /// `plane0` if the image is multi-planar. |
| pub regions: SmallVec<[ImageResolve; 1]>, |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl ResolveImageInfo { |
| /// Returns a `ResolveImageInfo` with the specified `src_image` and `dst_image`. |
| #[inline] |
| pub fn images(src_image: Arc<dyn ImageAccess>, dst_image: Arc<dyn ImageAccess>) -> Self { |
| let min_array_layers = src_image |
| .dimensions() |
| .array_layers() |
| .min(dst_image.dimensions().array_layers()); |
| let region = ImageResolve { |
| src_subresource: ImageSubresourceLayers { |
| array_layers: 0..min_array_layers, |
| ..src_image.subresource_layers() |
| }, |
| dst_subresource: ImageSubresourceLayers { |
| array_layers: 0..min_array_layers, |
| ..dst_image.subresource_layers() |
| }, |
| extent: { |
| let src_extent = src_image.dimensions().width_height_depth(); |
| let dst_extent = dst_image.dimensions().width_height_depth(); |
| |
| [ |
| src_extent[0].min(dst_extent[0]), |
| src_extent[1].min(dst_extent[1]), |
| src_extent[2].min(dst_extent[2]), |
| ] |
| }, |
| ..Default::default() |
| }; |
| |
| Self { |
| src_image, |
| src_image_layout: ImageLayout::TransferSrcOptimal, |
| dst_image, |
| dst_image_layout: ImageLayout::TransferDstOptimal, |
| regions: smallvec![region], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// A region of data to resolve between images. |
| #[derive(Clone, Debug)] |
| pub struct ImageResolve { |
| /// The subresource of `src_image` to resolve from. |
| /// |
| /// The default value is empty, which must be overridden. |
| pub src_subresource: ImageSubresourceLayers, |
| |
| /// The offset from the zero coordinate of `src_image` that resolving will start from. |
| /// |
| /// The default value is `[0; 3]`. |
| pub src_offset: [u32; 3], |
| |
| /// The subresource of `dst_image` to resolve into. |
| /// |
| /// The default value is empty, which must be overridden. |
| pub dst_subresource: ImageSubresourceLayers, |
| |
| /// The offset from the zero coordinate of `dst_image` that resolving will start from. |
| /// |
| /// The default value is `[0; 3]`. |
| pub dst_offset: [u32; 3], |
| |
| /// The extent of texels to resolve. |
| /// |
| /// The default value is `[0; 3]`, which must be overridden. |
| pub extent: [u32; 3], |
| |
| pub _ne: crate::NonExhaustive, |
| } |
| |
| impl Default for ImageResolve { |
| #[inline] |
| fn default() -> Self { |
| Self { |
| src_subresource: ImageSubresourceLayers { |
| aspects: ImageAspects::empty(), |
| mip_level: 0, |
| array_layers: 0..0, |
| }, |
| src_offset: [0; 3], |
| dst_subresource: ImageSubresourceLayers { |
| aspects: ImageAspects::empty(), |
| mip_level: 0, |
| array_layers: 0..0, |
| }, |
| dst_offset: [0; 3], |
| extent: [0; 3], |
| _ne: crate::NonExhaustive(()), |
| } |
| } |
| } |
| |
| /// Error that can happen when recording a copy command. |
| #[derive(Clone, Debug)] |
| pub enum CopyError { |
| SyncCommandBufferBuilderError(SyncCommandBufferBuilderError), |
| |
| RequirementNotMet { |
| required_for: &'static str, |
| requires_one_of: RequiresOneOf, |
| }, |
| |
| /// Operation forbidden inside of a render pass. |
| ForbiddenInsideRenderPass, |
| |
| /// The queue family doesn't allow this operation. |
| NotSupportedByQueueFamily, |
| |
| /// The array layer counts of the source and destination subresource ranges of a region do not |
| /// match. |
| ArrayLayerCountMismatch { |
| region_index: usize, |
| src_layer_count: u32, |
| dst_layer_count: u32, |
| }, |
| |
| /// The end of the range of accessed array layers of the subresource range of a region is |
| /// greater than the number of array layers in the image. |
| ArrayLayersOutOfRange { |
| resource: CopyErrorResource, |
| region_index: usize, |
| array_layers_range_end: u32, |
| image_array_layers: u32, |
| }, |
| |
| /// The aspects of the source and destination subresource ranges of a region do not match. |
| AspectsMismatch { |
| region_index: usize, |
| src_aspects: ImageAspects, |
| dst_aspects: ImageAspects, |
| }, |
| |
| /// The aspects of the subresource range of a region contain aspects that are not present |
| /// in the image, or that are not allowed. |
| AspectsNotAllowed { |
| resource: CopyErrorResource, |
| region_index: usize, |
| aspects: ImageAspects, |
| allowed_aspects: ImageAspects, |
| }, |
| |
| /// The buffer image height of a region is not a multiple of the required buffer alignment. |
| BufferImageHeightNotAligned { |
| resource: CopyErrorResource, |
| region_index: usize, |
| image_height: u32, |
| required_alignment: u32, |
| }, |
| |
| /// The buffer image height of a region is smaller than the image extent height. |
| BufferImageHeightTooSmall { |
| resource: CopyErrorResource, |
| region_index: usize, |
| image_height: u32, |
| min: u32, |
| }, |
| |
| /// The buffer row length of a region is not a multiple of the required buffer alignment. |
| BufferRowLengthNotAligned { |
| resource: CopyErrorResource, |
| region_index: usize, |
| row_length: u32, |
| required_alignment: u32, |
| }, |
| |
| /// The buffer row length of a region specifies a row of texels that is greater than 0x7FFFFFFF |
| /// bytes in size. |
| BufferRowLengthTooLarge { |
| resource: CopyErrorResource, |
| region_index: usize, |
| buffer_row_length: u32, |
| }, |
| |
| /// The buffer row length of a region is smaller than the image extent width. |
| BufferRowLengthTooSmall { |
| resource: CopyErrorResource, |
| region_index: usize, |
| row_length: u32, |
| min: u32, |
| }, |
| |
| /// Depth/stencil images are not supported by the queue family of this command buffer; a |
| /// graphics queue family is required. |
| DepthStencilNotSupportedByQueueFamily, |
| |
| /// The image extent of a region is not a multiple of the required image alignment. |
| ExtentNotAlignedForImage { |
| resource: CopyErrorResource, |
| region_index: usize, |
| extent: [u32; 3], |
| required_alignment: [u32; 3], |
| }, |
| |
| /// The chosen filter type does not support the dimensionality of the source image. |
| FilterNotSupportedForImageType, |
| |
| /// The chosen filter type does not support the format of the source image. |
| FilterNotSupportedByFormat, |
| |
| /// The format of an image is not supported for this operation. |
| FormatNotSupported { |
| resource: CopyErrorResource, |
| format: Format, |
| }, |
| |
| /// The format of the source image does not match the format of the destination image. |
| FormatsMismatch { |
| src_format: Format, |
| dst_format: Format, |
| }, |
| |
| /// The format of the source image subresource is not compatible with the format of the |
| /// destination image subresource. |
| FormatsNotCompatible { |
| src_format: Format, |
| dst_format: Format, |
| }, |
| |
| /// A specified image layout is not valid for this operation. |
| ImageLayoutInvalid { |
| resource: CopyErrorResource, |
| image_layout: ImageLayout, |
| }, |
| |
| /// The end of the range of accessed mip levels of the subresource range of a region is greater |
| /// than the number of mip levels in the image. |
| MipLevelsOutOfRange { |
| resource: CopyErrorResource, |
| region_index: usize, |
| mip_levels_range_end: u32, |
| image_mip_levels: u32, |
| }, |
| |
| /// An image does not have a required format feature. |
| MissingFormatFeature { |
| resource: CopyErrorResource, |
| format_feature: &'static str, |
| }, |
| |
| /// A resource did not have a required usage enabled. |
| MissingUsage { |
| resource: CopyErrorResource, |
| usage: &'static str, |
| }, |
| |
| /// A subresource range of a region specifies multiple aspects, but only one aspect can be |
| /// selected for the image. |
| MultipleAspectsNotAllowed { |
| resource: CopyErrorResource, |
| region_index: usize, |
| aspects: ImageAspects, |
| }, |
| |
| /// The buffer offset of a region is not a multiple of the required buffer alignment. |
| OffsetNotAlignedForBuffer { |
| resource: CopyErrorResource, |
| region_index: usize, |
| offset: DeviceSize, |
| required_alignment: DeviceSize, |
| }, |
| |
| /// The image offset of a region is not a multiple of the required image alignment. |
| OffsetNotAlignedForImage { |
| resource: CopyErrorResource, |
| region_index: usize, |
| offset: [u32; 3], |
| required_alignment: [u32; 3], |
| }, |
| |
| /// The image offsets of a region are not the values required for that axis ([0, 1]) for the |
| /// type of the image. |
| OffsetsInvalidForImageType { |
| resource: CopyErrorResource, |
| region_index: usize, |
| offsets: [u32; 2], |
| }, |
| |
| /// The source bounds of a region overlap with the destination bounds of a region. |
| OverlappingRegions { |
| src_region_index: usize, |
| dst_region_index: usize, |
| }, |
| |
| /// The source subresources of a region overlap with the destination subresources of a region, |
| /// but the source image layout does not equal the destination image layout. |
| OverlappingSubresourcesLayoutMismatch { |
| src_region_index: usize, |
| dst_region_index: usize, |
| src_image_layout: ImageLayout, |
| dst_image_layout: ImageLayout, |
| }, |
| |
| /// The end of the range of accessed byte offsets of a region is greater than the size of the |
| /// buffer. |
| RegionOutOfBufferBounds { |
| resource: CopyErrorResource, |
| region_index: usize, |
| offset_range_end: DeviceSize, |
| buffer_size: DeviceSize, |
| }, |
| |
| /// The end of the range of accessed texel offsets of a region is greater than the extent of |
| /// the selected subresource of the image. |
| RegionOutOfImageBounds { |
| resource: CopyErrorResource, |
| region_index: usize, |
| offset_range_end: [u32; 3], |
| subresource_extent: [u32; 3], |
| }, |
| |
| /// An image has a sample count that is not valid for this operation. |
| SampleCountInvalid { |
| resource: CopyErrorResource, |
| sample_count: SampleCount, |
| allowed_sample_counts: SampleCounts, |
| }, |
| |
| /// The source image has a different sample count than the destination image. |
| SampleCountMismatch { |
| src_sample_count: SampleCount, |
| dst_sample_count: SampleCount, |
| }, |
| } |
| |
| impl Error for CopyError { |
| fn source(&self) -> Option<&(dyn Error + 'static)> { |
| match self { |
| Self::SyncCommandBufferBuilderError(err) => Some(err), |
| _ => None, |
| } |
| } |
| } |
| |
| impl Display for CopyError { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { |
| match self { |
| Self::SyncCommandBufferBuilderError(_) => write!(f, "a SyncCommandBufferBuilderError"), |
| Self::RequirementNotMet { |
| required_for, |
| requires_one_of, |
| } => write!( |
| f, |
| "a requirement was not met for: {}; requires one of: {}", |
| required_for, requires_one_of, |
| ), |
| Self::ForbiddenInsideRenderPass => { |
| write!(f, "operation forbidden inside of a render pass") |
| } |
| Self::NotSupportedByQueueFamily => { |
| write!(f, "the queue family doesn't allow this operation") |
| } |
| Self::ArrayLayerCountMismatch { |
| region_index, |
| src_layer_count, |
| dst_layer_count, |
| } => write!( |
| f, |
| "the array layer counts of the source and destination subresource ranges of region \ |
| {} do not match (source: {}; destination: {})", |
| region_index, src_layer_count, dst_layer_count, |
| ), |
| Self::ArrayLayersOutOfRange { |
| resource, |
| region_index, |
| array_layers_range_end, |
| image_array_layers, |
| } => write!( |
| f, |
| "the end of the range of accessed array layers ({}) of the {} subresource range of \ |
| region {} is greater than the number of array layers in the {} image ({})", |
| array_layers_range_end, resource, region_index, resource, image_array_layers, |
| ), |
| Self::AspectsMismatch { |
| region_index, |
| src_aspects, |
| dst_aspects, |
| } => write!( |
| f, |
| "the aspects of the source and destination subresource ranges of region {} do not \ |
| match (source: {:?}; destination: {:?})", |
| region_index, src_aspects, dst_aspects, |
| ), |
| Self::AspectsNotAllowed { |
| resource, |
| region_index, |
| aspects, |
| allowed_aspects, |
| } => write!( |
| f, |
| "the aspects ({:?}) of the {} subresource range of region {} contain aspects that \ |
| are not present in the {} image, or that are not allowed ({:?})", |
| aspects, resource, region_index, resource, allowed_aspects, |
| ), |
| Self::BufferImageHeightNotAligned { |
| resource, |
| region_index, |
| image_height, |
| required_alignment, |
| } => write!( |
| f, |
| "the {} buffer image height ({}) of region {} is not a multiple of the required {} \ |
| buffer alignment ({})", |
| resource, image_height, region_index, resource, required_alignment, |
| ), |
| Self::BufferRowLengthTooLarge { |
| resource, |
| region_index, |
| buffer_row_length, |
| } => write!( |
| f, |
| "the {} buffer row length ({}) of region {} specifies a row of texels that is \ |
| greater than 0x7FFFFFFF bytes in size", |
| resource, buffer_row_length, region_index, |
| ), |
| Self::BufferImageHeightTooSmall { |
| resource, |
| region_index, |
| image_height, |
| min, |
| } => write!( |
| f, |
| "the {} buffer image height ({}) of region {} is smaller than the {} image extent \ |
| height ({})", |
| resource, image_height, region_index, resource, min, |
| ), |
| Self::BufferRowLengthNotAligned { |
| resource, |
| region_index, |
| row_length, |
| required_alignment, |
| } => write!( |
| f, |
| "the {} buffer row length ({}) of region {} is not a multiple of the required {} \ |
| buffer alignment ({})", |
| resource, row_length, region_index, resource, required_alignment, |
| ), |
| Self::BufferRowLengthTooSmall { |
| resource, |
| region_index, |
| row_length, |
| min, |
| } => write!( |
| f, |
| "the {} buffer row length length ({}) of region {} is smaller than the {} image \ |
| extent width ({})", |
| resource, row_length, region_index, resource, min, |
| ), |
| Self::DepthStencilNotSupportedByQueueFamily => write!( |
| f, |
| "depth/stencil images are not supported by the queue family of this command \ |
| buffer; a graphics queue family is required", |
| ), |
| Self::ExtentNotAlignedForImage { |
| resource, |
| region_index, |
| extent, |
| required_alignment, |
| } => write!( |
| f, |
| "the {} image extent ({:?}) of region {} is not a multiple of the required {} \ |
| image alignment ({:?})", |
| resource, extent, region_index, resource, required_alignment, |
| ), |
| Self::FilterNotSupportedForImageType => write!( |
| f, |
| "the chosen filter is not supported for the source image type", |
| ), |
| Self::FilterNotSupportedByFormat => write!( |
| f, |
| "the chosen filter is not supported by the format of the source image", |
| ), |
| Self::FormatNotSupported { resource, format } => write!( |
| f, |
| "the format of the {} image ({:?}) is not supported for this operation", |
| resource, format, |
| ), |
| Self::FormatsMismatch { |
| src_format, |
| dst_format, |
| } => write!( |
| f, |
| "the format of the source image ({:?}) does not match the format of the \ |
| destination image ({:?})", |
| src_format, dst_format, |
| ), |
| Self::FormatsNotCompatible { |
| src_format, |
| dst_format, |
| } => write!( |
| f, |
| "the format of the source image subresource ({:?}) is not compatible with the \ |
| format of the destination image subresource ({:?})", |
| src_format, dst_format, |
| ), |
| Self::ImageLayoutInvalid { |
| resource, |
| image_layout, |
| } => write!( |
| f, |
| "the specified {} image layout {:?} is not valid for this operation", |
| resource, image_layout, |
| ), |
| Self::MipLevelsOutOfRange { |
| resource, |
| region_index, |
| mip_levels_range_end, |
| image_mip_levels, |
| } => write!( |
| f, |
| "the end of the range of accessed mip levels ({}) of the {} subresource range of \ |
| region {} is not less than the number of mip levels in the {} image ({})", |
| mip_levels_range_end, resource, region_index, resource, image_mip_levels, |
| ), |
| Self::MissingFormatFeature { |
| resource, |
| format_feature, |
| } => write!( |
| f, |
| "the {} image does not have the required format feature {}", |
| resource, format_feature, |
| ), |
| Self::MissingUsage { resource, usage } => write!( |
| f, |
| "the {} resource did not have the required usage {} enabled", |
| resource, usage, |
| ), |
| Self::MultipleAspectsNotAllowed { |
| resource, |
| region_index, |
| aspects, |
| } => write!( |
| f, |
| "the {} subresource range of region {} specifies multiple aspects ({:?}), but only \ |
| one aspect can be selected for the {} image", |
| resource, region_index, aspects, resource, |
| ), |
| Self::OffsetNotAlignedForBuffer { |
| resource, |
| region_index, |
| offset, |
| required_alignment, |
| } => write!( |
| f, |
| "the {} buffer offset ({}) of region {} is not a multiple of the required {} \ |
| buffer alignment ({})", |
| resource, offset, region_index, resource, required_alignment, |
| ), |
| Self::OffsetNotAlignedForImage { |
| resource, |
| region_index, |
| offset, |
| required_alignment, |
| } => write!( |
| f, |
| "the {} image offset ({:?}) of region {} is not a multiple of the required {} \ |
| image alignment ({:?})", |
| resource, offset, region_index, resource, required_alignment, |
| ), |
| Self::OffsetsInvalidForImageType { |
| resource, |
| region_index, |
| offsets, |
| } => write!( |
| f, |
| "the {} image offsets ({:?}) of region {} are not the values required for that \ |
| axis ([0, 1]) for the type of the {} image", |
| resource, offsets, region_index, resource, |
| ), |
| Self::OverlappingRegions { |
| src_region_index, |
| dst_region_index, |
| } => write!( |
| f, |
| "the source bounds of region {} overlap with the destination bounds of region {}", |
| src_region_index, dst_region_index, |
| ), |
| Self::OverlappingSubresourcesLayoutMismatch { |
| src_region_index, |
| dst_region_index, |
| src_image_layout, |
| dst_image_layout, |
| } => write!( |
| f, |
| "the source subresources of region {} overlap with the destination subresources of \ |
| region {}, but the source image layout ({:?}) does not equal the destination image \ |
| layout ({:?})", |
| src_region_index, dst_region_index, src_image_layout, dst_image_layout, |
| ), |
| Self::RegionOutOfBufferBounds { |
| resource, |
| region_index, |
| offset_range_end, |
| buffer_size, |
| } => write!( |
| f, |
| "the end of the range of accessed {} byte offsets ({}) of region {} is greater \ |
| than the size of the {} buffer ({})", |
| resource, offset_range_end, region_index, resource, buffer_size, |
| ), |
| Self::RegionOutOfImageBounds { |
| resource, |
| region_index, |
| offset_range_end, |
| subresource_extent, |
| } => write!( |
| f, |
| "the end of the range of accessed {} texel offsets ({:?}) of region {} is greater \ |
| than the extent of the selected subresource of the {} image ({:?})", |
| resource, offset_range_end, region_index, resource, subresource_extent, |
| ), |
| Self::SampleCountInvalid { |
| resource, |
| sample_count, |
| allowed_sample_counts, |
| } => write!( |
| f, |
| "the {} image has a sample count ({:?}) that is not valid for this operation \ |
| ({:?})", |
| resource, sample_count, allowed_sample_counts, |
| ), |
| Self::SampleCountMismatch { |
| src_sample_count, |
| dst_sample_count, |
| } => write!( |
| f, |
| "the source image has a different sample count ({:?}) than the destination image \ |
| ({:?})", |
| src_sample_count, dst_sample_count, |
| ), |
| } |
| } |
| } |
| |
| impl From<SyncCommandBufferBuilderError> for CopyError { |
| fn from(err: SyncCommandBufferBuilderError) -> Self { |
| Self::SyncCommandBufferBuilderError(err) |
| } |
| } |
| |
| impl From<RequirementNotMet> for CopyError { |
| fn from(err: RequirementNotMet) -> Self { |
| Self::RequirementNotMet { |
| required_for: err.required_for, |
| requires_one_of: err.requires_one_of, |
| } |
| } |
| } |
| |
| /// Indicates which resource a `CopyError` applies to. |
| #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
| pub enum CopyErrorResource { |
| Source, |
| Destination, |
| } |
| |
| impl Display for CopyErrorResource { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { |
| match self { |
| Self::Source => write!(f, "source"), |
| Self::Destination => write!(f, "destination"), |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::format::Format; |
| |
| /// Computes the minimum required len in elements for buffer with image data in specified |
| /// format of specified size. |
| fn required_size_for_format(format: Format, extent: [u32; 3], layer_count: u32) -> DeviceSize { |
| let num_blocks = extent |
| .into_iter() |
| .zip(format.block_extent()) |
| .map(|(extent, block_extent)| { |
| let extent = extent as DeviceSize; |
| let block_extent = block_extent as DeviceSize; |
| (extent + block_extent - 1) / block_extent |
| }) |
| .product::<DeviceSize>() |
| * layer_count as DeviceSize; |
| let block_size = format |
| .block_size() |
| .expect("this format cannot accept pixels"); |
| num_blocks * block_size |
| } |
| |
| #[test] |
| fn test_required_len_for_format() { |
| // issue #1292 |
| assert_eq!( |
| required_size_for_format(Format::BC1_RGB_UNORM_BLOCK, [2048, 2048, 1], 1), |
| 2097152 |
| ); |
| // other test cases |
| assert_eq!( |
| required_size_for_format(Format::R8G8B8A8_UNORM, [2048, 2048, 1], 1), |
| 16777216 |
| ); |
| assert_eq!( |
| required_size_for_format(Format::R4G4_UNORM_PACK8, [512, 512, 1], 1), |
| 262144 |
| ); |
| assert_eq!( |
| required_size_for_format(Format::R8G8B8_USCALED, [512, 512, 1], 1), |
| 786432 |
| ); |
| assert_eq!( |
| required_size_for_format(Format::R32G32_UINT, [512, 512, 1], 1), |
| 2097152 |
| ); |
| assert_eq!( |
| required_size_for_format(Format::R32G32_UINT, [512, 512, 1], 1), |
| 2097152 |
| ); |
| assert_eq!( |
| required_size_for_format(Format::ASTC_8x8_UNORM_BLOCK, [512, 512, 1], 1), |
| 65536 |
| ); |
| assert_eq!( |
| required_size_for_format(Format::ASTC_12x12_SRGB_BLOCK, [512, 512, 1], 1), |
| 29584 |
| ); |
| } |
| } |