aboutsummaryrefslogtreecommitdiff
path: root/src/command_buffer/commands/copy.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command_buffer/commands/copy.rs')
-rw-r--r--src/command_buffer/commands/copy.rs5404
1 files changed, 5404 insertions, 0 deletions
diff --git a/src/command_buffer/commands/copy.rs b/src/command_buffer/commands/copy.rs
new file mode 100644
index 0000000..1911713
--- /dev/null
+++ b/src/command_buffer/commands/copy.rs
@@ -0,0 +1,5404 @@
+// 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(&copy_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(&copy_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(&copy_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(&copy_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: _,
+ } = &copy_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: _,
+ } = &copy_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: _,
+ } = &copy_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: _,
+ } = &copy_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, &copy_buffer_info);
+ } else {
+ (fns.khr_copy_commands2.cmd_copy_buffer2_khr)(self.handle, &copy_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, &copy_image_info);
+ } else {
+ (fns.khr_copy_commands2.cmd_copy_image2_khr)(self.handle, &copy_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, &copy_buffer_to_image_info);
+ } else {
+ (fns.khr_copy_commands2.cmd_copy_buffer_to_image2_khr)(
+ self.handle,
+ &copy_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, &copy_image_to_buffer_info);
+ } else {
+ (fns.khr_copy_commands2.cmd_copy_image_to_buffer2_khr)(
+ self.handle,
+ &copy_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
+ );
+ }
+}