blob: 1911713d267b0ae7554613248f35623dd8310711 [file] [log] [blame]
// 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
);
}
}