use crate::{
buffer::{BufferUsage, Subbuffer},
command_buffer::{
auto::Resource, sys::RecordingCommandBuffer, AutoCommandBufferBuilder, ResourceInCommand,
},
device::{Device, DeviceOwned, QueueFlags},
format::{Format, FormatFeatures},
image::{
mip_level_extent, sampler::Filter, Image, ImageAspects, ImageLayout,
ImageSubresourceLayers, ImageTiling, ImageType, ImageUsage, SampleCount,
},
sync::PipelineStageAccessFlags,
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, ValidationError, Version, VulkanObject,
};
use smallvec::{smallvec, SmallVec};
use std::{
cmp::{max, min},
mem::size_of,
sync::Arc,
};
impl<L> AutoCommandBufferBuilder<L> {
pub fn copy_buffer(
&mut self,
copy_buffer_info: impl Into<CopyBufferInfo>,
) -> Result<&mut Self, Box<ValidationError>> {
let copy_buffer_info = copy_buffer_info.into();
self.validate_copy_buffer(©_buffer_info)?;
Ok(unsafe { self.copy_buffer_unchecked(copy_buffer_info) })
}
fn validate_copy_buffer(
&self,
copy_buffer_info: &CopyBufferInfo,
) -> Result<(), Box<ValidationError>> {
self.inner.validate_copy_buffer(copy_buffer_info)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdCopyBuffer2-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn copy_buffer_unchecked(
&mut self,
copy_buffer_info: impl Into<CopyBufferInfo>,
) -> &mut Self {
let copy_buffer_info = copy_buffer_info.into();
let CopyBufferInfo {
src_buffer,
dst_buffer,
regions,
_ne: _,
} = ©_buffer_info;
self.add_command(
"copy_buffer",
regions
.iter()
.flat_map(|region| {
let &BufferCopy {
src_offset,
dst_offset,
size,
_ne: _,
} = region;
[
(
ResourceInCommand::Source.into(),
Resource::Buffer {
buffer: src_buffer.clone(),
range: src_offset..src_offset + size,
memory_access: PipelineStageAccessFlags::Copy_TransferRead,
},
),
(
ResourceInCommand::Destination.into(),
Resource::Buffer {
buffer: dst_buffer.clone(),
range: dst_offset..dst_offset + size,
memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
},
),
]
})
.collect(),
move |out: &mut RecordingCommandBuffer| {
unsafe { out.copy_buffer_unchecked(©_buffer_info) };
},
);
self
}
pub fn copy_image(
&mut self,
copy_image_info: CopyImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_copy_image(©_image_info)?;
Ok(unsafe { self.copy_image_unchecked(copy_image_info) })
}
fn validate_copy_image(
&self,
copy_image_info: &CopyImageInfo,
) -> Result<(), Box<ValidationError>> {
self.inner.validate_copy_image(copy_image_info)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdCopyImage2-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn copy_image_unchecked(&mut self, copy_image_info: CopyImageInfo) -> &mut Self {
let &CopyImageInfo {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
ref regions,
_ne: _,
} = ©_image_info;
self.add_command(
"copy_image",
regions
.iter()
.flat_map(|region| {
let &ImageCopy {
ref src_subresource,
src_offset: _,
ref dst_subresource,
dst_offset: _,
extent: _,
_ne: _,
} = region;
[
(
ResourceInCommand::Source.into(),
Resource::Image {
image: src_image.clone(),
subresource_range: src_subresource.clone().into(),
memory_access: PipelineStageAccessFlags::Copy_TransferRead,
start_layout: src_image_layout,
end_layout: src_image_layout,
},
),
(
ResourceInCommand::Destination.into(),
Resource::Image {
image: dst_image.clone(),
subresource_range: dst_subresource.clone().into(),
memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
start_layout: dst_image_layout,
end_layout: dst_image_layout,
},
),
]
})
.collect(),
move |out: &mut RecordingCommandBuffer| {
unsafe { out.copy_image_unchecked(©_image_info) };
},
);
self
}
pub fn copy_buffer_to_image(
&mut self,
copy_buffer_to_image_info: CopyBufferToImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_copy_buffer_to_image(©_buffer_to_image_info)?;
Ok(unsafe { self.copy_buffer_to_image_unchecked(copy_buffer_to_image_info) })
}
fn validate_copy_buffer_to_image(
&self,
copy_buffer_to_image_info: &CopyBufferToImageInfo,
) -> Result<(), Box<ValidationError>> {
self.inner
.validate_copy_buffer_to_image(copy_buffer_to_image_info)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdCopyBufferToImage2-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn copy_buffer_to_image_unchecked(
&mut self,
copy_buffer_to_image_info: CopyBufferToImageInfo,
) -> &mut Self {
let &CopyBufferToImageInfo {
ref src_buffer,
ref dst_image,
dst_image_layout,
ref regions,
_ne: _,
} = ©_buffer_to_image_info;
self.add_command(
"copy_buffer_to_image",
regions
.iter()
.flat_map(|region| {
let &BufferImageCopy {
buffer_offset,
buffer_row_length: _,
buffer_image_height: _,
ref image_subresource,
image_offset: _,
image_extent: _,
_ne: _,
} = region;
[
(
ResourceInCommand::Source.into(),
Resource::Buffer {
buffer: src_buffer.clone(),
range: buffer_offset
..buffer_offset + region.buffer_copy_size(dst_image.format()),
memory_access: PipelineStageAccessFlags::Copy_TransferRead,
},
),
(
ResourceInCommand::Destination.into(),
Resource::Image {
image: dst_image.clone(),
subresource_range: image_subresource.clone().into(),
memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
start_layout: dst_image_layout,
end_layout: dst_image_layout,
},
),
]
})
.collect(),
move |out: &mut RecordingCommandBuffer| {
unsafe { out.copy_buffer_to_image_unchecked(©_buffer_to_image_info) };
},
);
self
}
pub fn copy_image_to_buffer(
&mut self,
copy_image_to_buffer_info: CopyImageToBufferInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_copy_image_to_buffer(©_image_to_buffer_info)?;
Ok(unsafe { self.copy_image_to_buffer_unchecked(copy_image_to_buffer_info) })
}
fn validate_copy_image_to_buffer(
&self,
copy_image_to_buffer_info: &CopyImageToBufferInfo,
) -> Result<(), Box<ValidationError>> {
self.inner
.validate_copy_image_to_buffer(copy_image_to_buffer_info)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdCopyImageToBuffer2-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn copy_image_to_buffer_unchecked(
&mut self,
copy_image_to_buffer_info: CopyImageToBufferInfo,
) -> &mut Self {
let &CopyImageToBufferInfo {
ref src_image,
src_image_layout,
ref dst_buffer,
ref regions,
_ne: _,
} = ©_image_to_buffer_info;
self.add_command(
"copy_image_to_buffer",
regions
.iter()
.flat_map(|region| {
let &BufferImageCopy {
buffer_offset,
buffer_row_length: _,
buffer_image_height: _,
ref image_subresource,
image_offset: _,
image_extent: _,
_ne: _,
} = region;
[
(
ResourceInCommand::Source.into(),
Resource::Image {
image: src_image.clone(),
subresource_range: image_subresource.clone().into(),
memory_access: PipelineStageAccessFlags::Copy_TransferRead,
start_layout: src_image_layout,
end_layout: src_image_layout,
},
),
(
ResourceInCommand::Destination.into(),
Resource::Buffer {
buffer: dst_buffer.clone(),
range: buffer_offset
..buffer_offset + region.buffer_copy_size(src_image.format()),
memory_access: PipelineStageAccessFlags::Copy_TransferWrite,
},
),
]
})
.collect(),
move |out: &mut RecordingCommandBuffer| {
unsafe { out.copy_image_to_buffer_unchecked(©_image_to_buffer_info) };
},
);
self
}
pub fn blit_image(
&mut self,
blit_image_info: BlitImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_blit_image(&blit_image_info)?;
Ok(unsafe { self.blit_image_unchecked(blit_image_info) })
}
fn validate_blit_image(
&self,
blit_image_info: &BlitImageInfo,
) -> Result<(), Box<ValidationError>> {
self.inner.validate_blit_image(blit_image_info)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdBlitImage2-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn blit_image_unchecked(&mut self, blit_image_info: BlitImageInfo) -> &mut Self {
let &BlitImageInfo {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
ref regions,
filter: _,
_ne: _,
} = &blit_image_info;
self.add_command(
"blit_image",
regions
.iter()
.flat_map(|region| {
let &ImageBlit {
ref src_subresource,
src_offsets: _,
ref dst_subresource,
dst_offsets: _,
_ne: _,
} = region;
[
(
ResourceInCommand::Source.into(),
Resource::Image {
image: src_image.clone(),
subresource_range: src_subresource.clone().into(),
memory_access: PipelineStageAccessFlags::Blit_TransferRead,
start_layout: src_image_layout,
end_layout: src_image_layout,
},
),
(
ResourceInCommand::Destination.into(),
Resource::Image {
image: dst_image.clone(),
subresource_range: dst_subresource.clone().into(),
memory_access: PipelineStageAccessFlags::Blit_TransferWrite,
start_layout: dst_image_layout,
end_layout: dst_image_layout,
},
),
]
})
.collect(),
move |out: &mut RecordingCommandBuffer| {
unsafe { out.blit_image_unchecked(&blit_image_info) };
},
);
self
}
pub fn resolve_image(
&mut self,
resolve_image_info: ResolveImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_resolve_image(&resolve_image_info)?;
Ok(unsafe { self.resolve_image_unchecked(resolve_image_info) })
}
fn validate_resolve_image(
&self,
resolve_image_info: &ResolveImageInfo,
) -> Result<(), Box<ValidationError>> {
self.inner.validate_resolve_image(resolve_image_info)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdResolveImage2-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn resolve_image_unchecked(
&mut self,
resolve_image_info: ResolveImageInfo,
) -> &mut Self {
let &ResolveImageInfo {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
ref regions,
_ne: _,
} = &resolve_image_info;
self.add_command(
"resolve_image",
regions
.iter()
.flat_map(|region| {
let &ImageResolve {
ref src_subresource,
src_offset: _,
ref dst_subresource,
dst_offset: _,
extent: _,
_ne: _,
} = region;
[
(
ResourceInCommand::Source.into(),
Resource::Image {
image: src_image.clone(),
subresource_range: src_subresource.clone().into(),
memory_access: PipelineStageAccessFlags::Resolve_TransferRead,
start_layout: src_image_layout,
end_layout: src_image_layout,
},
),
(
ResourceInCommand::Destination.into(),
Resource::Image {
image: dst_image.clone(),
subresource_range: dst_subresource.clone().into(),
memory_access: PipelineStageAccessFlags::Resolve_TransferWrite,
start_layout: dst_image_layout,
end_layout: dst_image_layout,
},
),
]
})
.collect(),
move |out: &mut RecordingCommandBuffer| {
unsafe { out.resolve_image_unchecked(&resolve_image_info) };
},
);
self
}
}
impl RecordingCommandBuffer {
#[inline]
pub unsafe fn copy_buffer(
&mut self,
copy_buffer_info: &CopyBufferInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_copy_buffer(copy_buffer_info)?;
Ok(unsafe { self.copy_buffer_unchecked(copy_buffer_info) })
}
fn validate_copy_buffer(
&self,
copy_buffer_info: &CopyBufferInfo,
) -> Result<(), Box<ValidationError>> {
if !self
.queue_family_properties()
.queue_flags
.intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
{
return Err(Box::new(ValidationError {
problem: "the queue family of the command buffer does not support \
transfer, graphics or compute operations"
.into(),
vuids: &["VUID-vkCmdCopyBuffer2-commandBuffer-cmdpool"],
..Default::default()
}));
}
copy_buffer_info
.validate(self.device())
.map_err(|err| err.add_context("copy_buffer_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn copy_buffer_unchecked(&mut self, copy_buffer_info: &CopyBufferInfo) -> &mut Self {
if copy_buffer_info.regions.is_empty() {
return self;
}
let fns = self.device().fns();
if self.device().api_version() >= Version::V1_3
|| self.device().enabled_extensions().khr_copy_commands2
{
let regions_vk = copy_buffer_info.to_vk2_regions();
let copy_buffer_info_vk = copy_buffer_info.to_vk2(®ions_vk);
if self.device().api_version() >= Version::V1_3 {
unsafe { (fns.v1_3.cmd_copy_buffer2)(self.handle(), ©_buffer_info_vk) };
} else {
unsafe {
(fns.khr_copy_commands2.cmd_copy_buffer2_khr)(
self.handle(),
©_buffer_info_vk,
)
};
}
} else {
let regions_vk = copy_buffer_info.to_vk_regions();
let CopyBufferInfoVk {
src_buffer_vk,
dst_buffer_vk,
} = copy_buffer_info.to_vk();
unsafe {
(fns.v1_0.cmd_copy_buffer)(
self.handle(),
src_buffer_vk,
dst_buffer_vk,
regions_vk.len() as u32,
regions_vk.as_ptr(),
)
};
}
self
}
#[inline]
pub unsafe fn copy_image(
&mut self,
copy_image_info: &CopyImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_copy_image(copy_image_info)?;
Ok(unsafe { self.copy_image_unchecked(copy_image_info) })
}
fn validate_copy_image(
&self,
copy_image_info: &CopyImageInfo,
) -> Result<(), Box<ValidationError>> {
let queue_family_properties = self.queue_family_properties();
if !queue_family_properties
.queue_flags
.intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
{
return Err(Box::new(ValidationError {
problem: "the queue family of the command buffer does not support \
transfer, graphics or compute operations"
.into(),
vuids: &["VUID-vkCmdCopyImage2-commandBuffer-cmdpool"],
..Default::default()
}));
}
copy_image_info
.validate(self.device())
.map_err(|err| err.add_context("copy_image_info"))?;
let &CopyImageInfo {
ref src_image,
src_image_layout: _,
ref dst_image,
dst_image_layout: _,
ref regions,
_ne: _,
} = copy_image_info;
let src_image_format = src_image.format();
let src_image_format_subsampled_extent = src_image_format
.ycbcr_chroma_sampling()
.map_or(src_image.extent(), |s| {
s.subsampled_extent(src_image.extent())
});
let dst_image_format = dst_image.format();
let dst_image_format_subsampled_extent = dst_image_format
.ycbcr_chroma_sampling()
.map_or(dst_image.extent(), |s| {
s.subsampled_extent(dst_image.extent())
});
let min_image_transfer_granularity =
(queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
(queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
let src_granularity = if src_image_format.compression().is_some() {
let granularity = queue_family_properties.min_image_transfer_granularity;
let block_extent = src_image_format.block_extent();
[
granularity[0] * block_extent[0],
granularity[1] * block_extent[1],
granularity[2] * block_extent[2],
]
} else {
queue_family_properties.min_image_transfer_granularity
};
let dst_granularity = if dst_image_format.compression().is_some() {
let granularity = queue_family_properties.min_image_transfer_granularity;
let block_extent = dst_image_format.block_extent();
[
granularity[0] * block_extent[0],
granularity[1] * block_extent[1],
granularity[2] * block_extent[2],
]
} else {
queue_family_properties.min_image_transfer_granularity
};
(src_granularity, dst_granularity)
})
});
if min_image_transfer_granularity.is_some() {
for (region_index, region) in regions.iter().enumerate() {
let &ImageCopy {
ref src_subresource,
src_offset,
ref dst_subresource,
dst_offset,
extent,
_ne: _,
} = region;
if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity {
let mut src_subresource_extent =
mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap();
if matches!(
src_subresource.aspects,
ImageAspects::PLANE_1 | ImageAspects::PLANE_2
) {
src_subresource_extent = src_image_format_subsampled_extent;
}
let mut dst_subresource_extent =
mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap();
if matches!(
dst_subresource.aspects,
ImageAspects::PLANE_1 | ImageAspects::PLANE_2
) {
dst_subresource_extent = dst_image_format_subsampled_extent;
}
if let Some((src_granularity, dst_granularity)) =
&min_image_transfer_granularity
{
for i in 0..3 {
if src_offset[i] % src_granularity[i] != 0 {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is not `[0; 3]`, but \
`regions[{}].src_offset[{1}]` is not a multiple of \
`min_image_transfer_granularity[{1}]` texel blocks",
region_index, i,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"],
..Default::default()
}));
}
if src_offset[i] + extent[i] != src_subresource_extent[i]
&& extent[i] % src_granularity[i] != 0
{
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is not `[0; 3]`, and \
`regions[{0}].src_offset[{1}] + regions[{0}].extent[{1}]` \
is not equal to coordinate {1} of the extent of the \
subresource of `src_image` selected by \
`regions[{0}].src_subresource`, but \
`regions[{}].extent[{1}]` is not a multiple of \
`min_image_transfer_granularity[{1}]` texel blocks",
region_index, i,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"],
..Default::default()
}));
}
}
for i in 0..3 {
if dst_offset[i] % dst_granularity[i] != 0 {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is not `[0; 3]`, but \
`regions[{}].dst_offset[{1}]` is not a multiple of \
`min_image_transfer_granularity[{1}]` texel blocks",
region_index, i,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"],
..Default::default()
}));
}
if dst_offset[i] + extent[i] != dst_subresource_extent[i]
&& extent[i] % dst_granularity[i] != 0
{
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is not `[0; 3]`, and \
`regions[{0}].dst_offset[{1}] + regions[{0}].extent[{1}]` \
is not equal to coordinate {1} of the extent of the \
subresource of `dst_image` selected by \
`regions[{0}].dst_subresource`, but \
`regions[{}].extent[{1}]` is not a multiple of \
`min_image_transfer_granularity[{1}]` texel blocks",
region_index, i,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"],
..Default::default()
}));
}
}
} else {
for i in 0..3 {
if src_offset[i] != 0 {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is `[0; 3]`, but \
`regions[{}].src_offset[{}]` is not 0",
region_index, i,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"],
..Default::default()
}));
}
if src_offset[i] + extent[i] != src_subresource_extent[i] {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is `[0; 3]`, but \
`regions[{0}].src_offset[{1}] + regions[{0}].extent[{1}]` \
is not equal to coordinate {1} of the extent of the \
subresource of `src_image` selected by \
`regions[{0}].src_subresource`",
region_index, i,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcOffset-01783"],
..Default::default()
}));
}
}
for i in 0..3 {
if dst_offset[i] != 0 {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is `[0; 3]`, but \
`regions[{}].dst_offset[{}]` is not 0",
region_index, i,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"],
..Default::default()
}));
}
if dst_offset[i] + extent[i] != dst_subresource_extent[i] {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is `[0; 3]`, but \
`regions[{0}].dst_offset[{1}] + regions[{0}].extent[{1}]` \
is not equal to coordinate {1} of the extent of the \
subresource of `dst_image` selected by \
`regions[{0}].dst_subresource`",
region_index, i,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstOffset-01784"],
..Default::default()
}));
}
}
}
}
}
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn copy_image_unchecked(&mut self, copy_image_info: &CopyImageInfo) -> &mut Self {
if copy_image_info.regions.is_empty() {
return self;
}
let fns = self.device().fns();
if self.device().api_version() >= Version::V1_3
|| self.device().enabled_extensions().khr_copy_commands2
{
let regions_vk = copy_image_info.to_vk2_regions();
let copy_image_info_vk = copy_image_info.to_vk2(®ions_vk);
if self.device().api_version() >= Version::V1_3 {
unsafe { (fns.v1_3.cmd_copy_image2)(self.handle(), ©_image_info_vk) };
} else {
unsafe {
(fns.khr_copy_commands2.cmd_copy_image2_khr)(self.handle(), ©_image_info_vk)
};
}
} else {
let regions_vk = copy_image_info.to_vk_regions();
let CopyImageInfoVk {
src_image_vk,
src_image_layout_vk,
dst_image_vk,
dst_image_layout_vk,
} = copy_image_info.to_vk();
unsafe {
(fns.v1_0.cmd_copy_image)(
self.handle(),
src_image_vk,
src_image_layout_vk,
dst_image_vk,
dst_image_layout_vk,
regions_vk.len() as u32,
regions_vk.as_ptr(),
)
};
}
self
}
#[inline]
pub unsafe fn copy_buffer_to_image(
&mut self,
copy_buffer_to_image_info: &CopyBufferToImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_copy_buffer_to_image(copy_buffer_to_image_info)?;
Ok(unsafe { self.copy_buffer_to_image_unchecked(copy_buffer_to_image_info) })
}
fn validate_copy_buffer_to_image(
&self,
copy_buffer_to_image_info: &CopyBufferToImageInfo,
) -> Result<(), Box<ValidationError>> {
let queue_family_properties = self.queue_family_properties();
if !queue_family_properties
.queue_flags
.intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
{
return Err(Box::new(ValidationError {
problem: "the queue family of the command buffer does not support \
transfer, graphics or compute operations"
.into(),
vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-cmdpool"],
..Default::default()
}));
}
copy_buffer_to_image_info
.validate(self.device())
.map_err(|err| err.add_context("copy_buffer_to_image_info"))?;
let &CopyBufferToImageInfo {
src_buffer: _,
ref dst_image,
dst_image_layout: _,
ref regions,
_ne,
} = copy_buffer_to_image_info;
let dst_image_format = dst_image.format();
let dst_image_format_subsampled_extent = dst_image_format
.ycbcr_chroma_sampling()
.map_or(dst_image.extent(), |s| {
s.subsampled_extent(dst_image.extent())
});
let min_image_transfer_granularity =
(queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
(queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
if dst_image_format.compression().is_some() {
let granularity = queue_family_properties.min_image_transfer_granularity;
let block_extent = dst_image_format.block_extent();
[
granularity[0] * block_extent[0],
granularity[1] * block_extent[1],
granularity[2] * block_extent[2],
]
} else {
queue_family_properties.min_image_transfer_granularity
}
})
});
let queue_family_no_graphics = !queue_family_properties
.queue_flags
.intersects(QueueFlags::GRAPHICS);
let queue_family_no_compute = !queue_family_properties
.queue_flags
.intersects(QueueFlags::COMPUTE);
if min_image_transfer_granularity.is_some() || queue_family_no_graphics {
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;
if queue_family_no_graphics {
if queue_family_no_compute && buffer_offset % 4 != 0 {
return Err(Box::new(ValidationError {
context: "create_info".into(),
problem: format!(
"the queue family of the command buffer does not support \
graphics or compute operations, but \
`regions[{}].buffer_offset` is not a multiple of 4",
region_index
)
.into(),
vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-07737"],
..Default::default()
}));
}
if image_subresource
.aspects
.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
{
return Err(Box::new(ValidationError {
context: "create_info".into(),
problem: format!(
"the queue family of the command buffer does not support \
graphics operations, but \
`regions[{}].image_subresource.aspects` contains \
`ImageAspects::DEPTH` or `ImageAspects::STENCIL`",
region_index
)
.into(),
vuids: &["VUID-vkCmdCopyBufferToImage2-commandBuffer-07739"],
..Default::default()
}));
}
}
if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity {
let mut image_subresource_extent =
mip_level_extent(dst_image.extent(), image_subresource.mip_level).unwrap();
if matches!(
image_subresource.aspects,
ImageAspects::PLANE_1 | ImageAspects::PLANE_2
) {
image_subresource_extent = dst_image_format_subsampled_extent;
}
if let Some(dst_granularity) = &min_image_transfer_granularity {
for i in 0..3 {
if image_offset[i] % dst_granularity[i] != 0 {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is not `[0; 3]`, but \
`regions[{}].image_offset[{1}]` is not a multiple of \
`min_image_transfer_granularity[{1}]` texel blocks",
region_index, i,
)
.into(),
vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"],
..Default::default()
}));
}
if image_offset[i] + image_extent[i] != image_subresource_extent[i]
&& image_extent[i] % dst_granularity[i] != 0
{
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is not `[0; 3]`, and \
`regions[{0}].image_offset[{1}] + \
regions[{0}].image_extent[{1}]` \
is not equal to coordinate {1} of the extent of the \
subresource of `dst_image` selected by \
`regions[{0}].image_subresource`, but \
`regions[{}].image_extent[{1}]` is not a multiple of \
`min_image_transfer_granularity[{1}]` texel blocks",
region_index, i,
)
.into(),
vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"],
..Default::default()
}));
}
}
} else {
for i in 0..3 {
if image_offset[i] != 0 {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is `[0; 3]`, but \
`regions[{}].image_offset[{}]` is not 0",
region_index, i,
)
.into(),
vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"],
..Default::default()
}));
}
if image_offset[i] + image_extent[i] != image_subresource_extent[i] {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is `[0; 3]`, but \
`regions[{0}].image_offset[{1}] + \
regions[{0}].image_extent[{1}]` \
is not equal to coordinate {1} of the extent of the \
subresource of `dst_image` selected by \
`regions[{0}].image_subresource`",
region_index, i,
)
.into(),
vuids: &["VUID-vkCmdCopyBufferToImage2-imageOffset-07738"],
..Default::default()
}));
}
}
}
}
}
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn copy_buffer_to_image_unchecked(
&mut self,
copy_buffer_to_image_info: &CopyBufferToImageInfo,
) -> &mut Self {
if copy_buffer_to_image_info.regions.is_empty() {
return self;
}
let fns = self.device().fns();
if self.device().api_version() >= Version::V1_3
|| self.device().enabled_extensions().khr_copy_commands2
{
let regions_vk = copy_buffer_to_image_info.to_vk2_regions();
let copy_buffer_to_image_info_vk = copy_buffer_to_image_info.to_vk2(®ions_vk);
if self.device().api_version() >= Version::V1_3 {
unsafe {
(fns.v1_3.cmd_copy_buffer_to_image2)(
self.handle(),
©_buffer_to_image_info_vk,
)
};
} else {
unsafe {
(fns.khr_copy_commands2.cmd_copy_buffer_to_image2_khr)(
self.handle(),
©_buffer_to_image_info_vk,
)
};
}
} else {
let regions_vk = copy_buffer_to_image_info.to_vk_regions();
let CopyBufferToImageInfoVk {
src_buffer_vk,
dst_image_vk,
dst_image_layout_vk,
} = copy_buffer_to_image_info.to_vk();
unsafe {
(fns.v1_0.cmd_copy_buffer_to_image)(
self.handle(),
src_buffer_vk,
dst_image_vk,
dst_image_layout_vk,
regions_vk.len() as u32,
regions_vk.as_ptr(),
)
};
}
self
}
#[inline]
pub unsafe fn copy_image_to_buffer(
&mut self,
copy_image_to_buffer_info: &CopyImageToBufferInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_copy_image_to_buffer(copy_image_to_buffer_info)?;
Ok(unsafe { self.copy_image_to_buffer_unchecked(copy_image_to_buffer_info) })
}
fn validate_copy_image_to_buffer(
&self,
copy_image_to_buffer_info: &CopyImageToBufferInfo,
) -> Result<(), Box<ValidationError>> {
let queue_family_properties = self.queue_family_properties();
if !queue_family_properties
.queue_flags
.intersects(QueueFlags::TRANSFER | QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
{
return Err(Box::new(ValidationError {
problem: "the queue family of the command buffer does not support \
transfer, graphics or compute operations"
.into(),
vuids: &["VUID-vkCmdCopyImageToBuffer2-commandBuffer-cmdpool"],
..Default::default()
}));
}
copy_image_to_buffer_info
.validate(self.device())
.map_err(|err| err.add_context("copy_image_to_buffer_info"))?;
let &CopyImageToBufferInfo {
ref src_image,
src_image_layout: _,
dst_buffer: _,
ref regions,
_ne,
} = copy_image_to_buffer_info;
let src_image_format = src_image.format();
let src_image_format_subsampled_extent = src_image_format
.ycbcr_chroma_sampling()
.map_or(src_image.extent(), |s| {
s.subsampled_extent(src_image.extent())
});
let min_image_transfer_granularity =
(queue_family_properties.min_image_transfer_granularity != [1; 3]).then(|| {
(queue_family_properties.min_image_transfer_granularity != [0; 3]).then(|| {
if src_image_format.compression().is_some() {
let granularity = queue_family_properties.min_image_transfer_granularity;
let block_extent = src_image_format.block_extent();
[
granularity[0] * block_extent[0],
granularity[1] * block_extent[1],
granularity[2] * block_extent[2],
]
} else {
queue_family_properties.min_image_transfer_granularity
}
})
});
let queue_family_no_graphics = !queue_family_properties
.queue_flags
.intersects(QueueFlags::GRAPHICS);
let queue_family_no_compute = !queue_family_properties
.queue_flags
.intersects(QueueFlags::COMPUTE);
if min_image_transfer_granularity.is_some() || queue_family_no_graphics {
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;
if queue_family_no_graphics && queue_family_no_compute && buffer_offset % 4 != 0 {
return Err(Box::new(ValidationError {
context: "create_info".into(),
problem: format!(
"the queue family of the command buffer does not support \
graphics or compute operations, but \
`regions[{}].buffer_offset` is not a multiple of 4",
region_index
)
.into(),
vuids: &["VUID-vkCmdCopyImageToBuffer2-commandBuffer-07746"],
..Default::default()
}));
}
if let Some(min_image_transfer_granularity) = &min_image_transfer_granularity {
let mut image_subresource_extent =
mip_level_extent(src_image.extent(), image_subresource.mip_level).unwrap();
if matches!(
image_subresource.aspects,
ImageAspects::PLANE_1 | ImageAspects::PLANE_2
) {
image_subresource_extent = src_image_format_subsampled_extent;
}
if let Some(src_granularity) = &min_image_transfer_granularity {
for i in 0..3 {
if image_offset[i] % src_granularity[i] != 0 {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is not `[0; 3]`, but \
`regions[{}].image_offset[{1}]` is not a multiple of \
`min_image_transfer_granularity[{1}]` texel blocks",
region_index, i,
)
.into(),
vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"],
..Default::default()
}));
}
if image_offset[i] + image_extent[i] != image_subresource_extent[i]
&& image_extent[i] % src_granularity[i] != 0
{
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is not `[0; 3]`, and \
`regions[{0}].image_offset[{1}] + \
regions[{0}].image_extent[{1}]` \
is not equal to coordinate {1} of the extent of the \
subresource of `src_image` selected by \
`regions[{0}].image_subresource`, but \
`regions[{}].image_extent[{1}]` is not a multiple of \
`min_image_transfer_granularity[{1}]` texel blocks",
region_index, i,
)
.into(),
vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"],
..Default::default()
}));
}
}
} else {
for i in 0..3 {
if image_offset[i] != 0 {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is `[0; 3]`, but \
`regions[{}].image_offset[{}]` is not 0",
region_index, i,
)
.into(),
vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"],
..Default::default()
}));
}
if image_offset[i] + image_extent[i] != image_subresource_extent[i] {
return Err(Box::new(ValidationError {
context: "copy_image_info".into(),
problem: format!(
"the `min_image_transfer_granularity` property of the \
queue family of the command buffer is `[0; 3]`, but \
`regions[{0}].image_offset[{1}] + \
regions[{0}].image_extent[{1}]` \
is not equal to coordinate {1} of the extent of the \
subresource of `src_image` selected by \
`regions[{0}].image_subresource`",
region_index, i,
)
.into(),
vuids: &["VUID-vkCmdCopyImageToBuffer2-imageOffset-07747"],
..Default::default()
}));
}
}
}
}
}
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn copy_image_to_buffer_unchecked(
&mut self,
copy_image_to_buffer_info: &CopyImageToBufferInfo,
) -> &mut Self {
if copy_image_to_buffer_info.regions.is_empty() {
return self;
}
let fns = self.device().fns();
if self.device().api_version() >= Version::V1_3
|| self.device().enabled_extensions().khr_copy_commands2
{
let regions_vk = copy_image_to_buffer_info.to_vk2_regions();
let copy_image_to_buffer_info_vk = copy_image_to_buffer_info.to_vk2(®ions_vk);
if self.device().api_version() >= Version::V1_3 {
unsafe {
(fns.v1_3.cmd_copy_image_to_buffer2)(
self.handle(),
©_image_to_buffer_info_vk,
)
};
} else {
unsafe {
(fns.khr_copy_commands2.cmd_copy_image_to_buffer2_khr)(
self.handle(),
©_image_to_buffer_info_vk,
)
};
}
} else {
let regions_vk = copy_image_to_buffer_info.to_vk_regions();
let CopyImageToBufferInfoVk {
src_image_vk,
src_image_layout_vk,
dst_buffer_vk,
} = copy_image_to_buffer_info.to_vk();
unsafe {
(fns.v1_0.cmd_copy_image_to_buffer)(
self.handle(),
src_image_vk,
src_image_layout_vk,
dst_buffer_vk,
regions_vk.len() as u32,
regions_vk.as_ptr(),
)
};
}
self
}
#[inline]
pub unsafe fn blit_image(
&mut self,
blit_image_info: &BlitImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_blit_image(blit_image_info)?;
Ok(unsafe { self.blit_image_unchecked(blit_image_info) })
}
fn validate_blit_image(
&self,
blit_image_info: &BlitImageInfo,
) -> Result<(), Box<ValidationError>> {
if !self
.queue_family_properties()
.queue_flags
.intersects(QueueFlags::GRAPHICS)
{
return Err(Box::new(ValidationError {
problem: "the queue family of the command buffer does not support \
graphics operations"
.into(),
vuids: &["VUID-vkCmdBlitImage2-commandBuffer-cmdpool"],
..Default::default()
}));
}
blit_image_info
.validate(self.device())
.map_err(|err| err.add_context("blit_image_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn blit_image_unchecked(&mut self, blit_image_info: &BlitImageInfo) -> &mut Self {
if blit_image_info.regions.is_empty() {
return self;
}
let fns = self.device().fns();
if self.device().api_version() >= Version::V1_3
|| self.device().enabled_extensions().khr_copy_commands2
{
let regions_vk = blit_image_info.to_vk2_regions();
let blit_image_info_vk = blit_image_info.to_vk2(®ions_vk);
if self.device().api_version() >= Version::V1_3 {
unsafe { (fns.v1_3.cmd_blit_image2)(self.handle(), &blit_image_info_vk) };
} else {
unsafe {
(fns.khr_copy_commands2.cmd_blit_image2_khr)(self.handle(), &blit_image_info_vk)
};
}
} else {
let regions_vk = blit_image_info.to_vk_regions();
let BlitImageInfoVk {
src_image_vk,
src_image_layout_vk,
dst_image_vk,
dst_image_layout_vk,
filter_vk,
} = blit_image_info.to_vk();
unsafe {
(fns.v1_0.cmd_blit_image)(
self.handle(),
src_image_vk,
src_image_layout_vk,
dst_image_vk,
dst_image_layout_vk,
regions_vk.len() as u32,
regions_vk.as_ptr(),
filter_vk,
)
};
}
self
}
#[inline]
pub unsafe fn resolve_image(
&mut self,
resolve_image_info: &ResolveImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_resolve_image(resolve_image_info)?;
Ok(unsafe { self.resolve_image_unchecked(resolve_image_info) })
}
fn validate_resolve_image(
&self,
resolve_image_info: &ResolveImageInfo,
) -> Result<(), Box<ValidationError>> {
if !self
.queue_family_properties()
.queue_flags
.intersects(QueueFlags::GRAPHICS)
{
return Err(Box::new(ValidationError {
problem: "the queue family of the command buffer does not support \
graphics operations"
.into(),
vuids: &["VUID-vkCmdResolveImage2-commandBuffer-cmdpool"],
..Default::default()
}));
}
resolve_image_info
.validate(self.device())
.map_err(|err| err.add_context("resolve_image_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn resolve_image_unchecked(
&mut self,
resolve_image_info: &ResolveImageInfo,
) -> &mut Self {
if resolve_image_info.regions.is_empty() {
return self;
}
let fns = self.device().fns();
if self.device().api_version() >= Version::V1_3
|| self.device().enabled_extensions().khr_copy_commands2
{
let regions_vk = resolve_image_info.to_vk2_regions();
let resolve_image_info_vk = resolve_image_info.to_vk2(®ions_vk);
if self.device().api_version() >= Version::V1_3 {
unsafe { (fns.v1_3.cmd_resolve_image2)(self.handle(), &resolve_image_info_vk) };
} else {
unsafe {
(fns.khr_copy_commands2.cmd_resolve_image2_khr)(
self.handle(),
&resolve_image_info_vk,
)
};
}
} else {
let regions_vk = resolve_image_info.to_vk_regions();
let ResolveImageInfoVk {
src_image_vk,
src_image_layout_vk,
dst_image_vk,
dst_image_layout_vk,
} = resolve_image_info.to_vk();
unsafe {
(fns.v1_0.cmd_resolve_image)(
self.handle(),
src_image_vk,
src_image_layout_vk,
dst_image_vk,
dst_image_layout_vk,
regions_vk.len() as u32,
regions_vk.as_ptr(),
)
};
}
self
}
}
#[derive(Clone, Debug)]
pub struct CopyBufferInfo {
pub src_buffer: Subbuffer<[u8]>,
pub dst_buffer: Subbuffer<[u8]>,
pub regions: SmallVec<[BufferCopy; 1]>,
pub _ne: crate::NonExhaustive,
}
impl CopyBufferInfo {
#[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(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_buffer,
ref dst_buffer,
ref regions,
_ne: _,
} = self;
assert_eq!(device, src_buffer.device().as_ref());
assert_eq!(device, dst_buffer.device().as_ref());
if !src_buffer
.buffer()
.usage()
.intersects(BufferUsage::TRANSFER_SRC)
{
return Err(Box::new(ValidationError {
context: "src_buffer.buffer().usage()".into(),
problem: "does not contain `BufferUsage::TRANSFER_SRC`".into(),
vuids: &["VUID-VkCopyBufferInfo2-srcBuffer-00118"],
..Default::default()
}));
}
if !dst_buffer
.buffer()
.usage()
.intersects(BufferUsage::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
context: "dst_buffer.buffer().usage()".into(),
problem: "does not contain `BufferUsage::TRANSFER_DST`".into(),
vuids: &["VUID-VkCopyBufferInfo2-dstBuffer-00120"],
..Default::default()
}));
}
let same_buffer = src_buffer.buffer() == dst_buffer.buffer();
let mut overlap_indices = None;
for (region_index, region) in regions.iter().enumerate() {
region
.validate(device)
.map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
let &BufferCopy {
src_offset,
dst_offset,
size,
_ne: _,
} = region;
if src_offset + size > src_buffer.size() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset + regions[{0}].size` is greater than \
`src_buffer.size()`",
region_index
)
.into(),
vuids: &[
"VUID-VkCopyBufferInfo2-srcOffset-00113",
"VUID-VkCopyBufferInfo2-size-00115",
],
..Default::default()
}));
}
if dst_offset + size > dst_buffer.size() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset + regions[{0}].size` is greater than \
`dst_buffer.size()`",
region_index
)
.into(),
vuids: &[
"VUID-VkCopyBufferInfo2-dstOffset-00114",
"VUID-VkCopyBufferInfo2-size-00116",
],
..Default::default()
}));
}
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 {
continue;
}
overlap_indices = Some((src_region_index, dst_region_index));
}
}
}
if let Some((src_region_index, dst_region_index)) = overlap_indices {
return Err(Box::new(ValidationError {
problem: format!(
"`src_buffer.buffer()` is equal to `dst_buffer.buffer()`, and \
the source of `regions[{}]` overlaps with the destination of `regions[{}]`",
src_region_index, dst_region_index
)
.into(),
vuids: &["VUID-VkCopyBufferInfo2-pRegions-00117"],
..Default::default()
}));
}
Ok(())
}
pub(crate) fn to_vk2<'a>(
&self,
regions_vk: &'a [ash::vk::BufferCopy2<'static>],
) -> ash::vk::CopyBufferInfo2<'a> {
let &Self {
ref src_buffer,
ref dst_buffer,
regions: _,
_ne: _,
} = self;
ash::vk::CopyBufferInfo2::default()
.src_buffer(src_buffer.buffer().handle())
.dst_buffer(dst_buffer.buffer().handle())
.regions(regions_vk)
}
pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::BufferCopy2<'static>; 8]> {
let &Self {
ref src_buffer,
ref dst_buffer,
ref regions,
_ne: _,
} = self;
regions
.iter()
.map(|region| {
let mut region_vk = region.to_vk2();
region_vk.src_offset += src_buffer.offset();
region_vk.dst_offset += dst_buffer.offset();
region_vk
})
.collect()
}
pub(crate) fn to_vk(&self) -> CopyBufferInfoVk {
let &Self {
ref src_buffer,
ref dst_buffer,
regions: _,
_ne: _,
} = self;
CopyBufferInfoVk {
src_buffer_vk: src_buffer.buffer().handle(),
dst_buffer_vk: dst_buffer.buffer().handle(),
}
}
pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::BufferCopy; 8]> {
let &Self {
ref src_buffer,
ref dst_buffer,
ref regions,
_ne: _,
} = self;
regions
.iter()
.map(|region| {
let mut region_vk = region.to_vk();
region_vk.src_offset += src_buffer.offset();
region_vk.dst_offset += dst_buffer.offset();
region_vk
})
.collect()
}
}
pub(crate) struct CopyBufferInfoVk {
pub(crate) src_buffer_vk: ash::vk::Buffer,
pub(crate) dst_buffer_vk: ash::vk::Buffer,
}
#[derive(Clone, Debug)]
pub struct CopyBufferInfoTyped<T> {
pub src_buffer: Subbuffer<[T]>,
pub dst_buffer: Subbuffer<[T]>,
pub regions: SmallVec<[BufferCopy; 1]>,
pub _ne: crate::NonExhaustive,
}
impl<T> CopyBufferInfoTyped<T> {
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(()),
}
}
}
#[derive(Clone, Debug)]
pub struct BufferCopy {
pub src_offset: DeviceSize,
pub dst_offset: DeviceSize,
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(()),
}
}
}
impl BufferCopy {
pub(crate) fn validate(&self, _device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
src_offset: _,
dst_offset: _,
size,
_ne: _,
} = self;
if size == 0 {
return Err(Box::new(ValidationError {
context: "size".into(),
problem: "is zero".into(),
vuids: &["VUID-VkBufferCopy2-size-01988"],
..Default::default()
}));
}
Ok(())
}
pub(crate) fn to_vk2(&self) -> ash::vk::BufferCopy2<'static> {
let &Self {
src_offset,
dst_offset,
size,
_ne,
} = self;
ash::vk::BufferCopy2::default()
.src_offset(src_offset)
.dst_offset(dst_offset)
.size(size)
}
pub(crate) fn to_vk(&self) -> ash::vk::BufferCopy {
let &Self {
src_offset,
dst_offset,
size,
_ne,
} = self;
ash::vk::BufferCopy {
src_offset,
dst_offset,
size,
}
}
}
#[derive(Clone, Debug)]
pub struct CopyImageInfo {
pub src_image: Arc<Image>,
pub src_image_layout: ImageLayout,
pub dst_image: Arc<Image>,
pub dst_image_layout: ImageLayout,
pub regions: SmallVec<[ImageCopy; 1]>,
pub _ne: crate::NonExhaustive,
}
impl CopyImageInfo {
#[inline]
pub fn images(src_image: Arc<Image>, dst_image: Arc<Image>) -> Self {
let min_array_layers = src_image.array_layers().min(dst_image.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.extent();
let dst_extent = dst_image.extent();
[
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(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
ref regions,
_ne: _,
} = self;
src_image_layout.validate_device(device).map_err(|err| {
err.add_context("src_image_layout")
.set_vuids(&["VUID-VkCopyImageInfo2-srcImageLayout-parameter"])
})?;
dst_image_layout.validate_device(device).map_err(|err| {
err.add_context("dst_image_layout")
.set_vuids(&["VUID-VkCopyImageInfo2-dstImageLayout-parameter"])
})?;
assert_eq!(device, src_image.device().as_ref());
assert_eq!(device, dst_image.device().as_ref());
let src_image_format = src_image.format();
let src_image_format_aspects = src_image_format.aspects();
let src_image_format_planes = src_image_format.planes();
let src_image_format_subsampled_extent = src_image_format
.ycbcr_chroma_sampling()
.map_or(src_image.extent(), |s| {
s.subsampled_extent(src_image.extent())
});
let dst_image_format = dst_image.format();
let dst_image_format_aspects = dst_image_format.aspects();
let dst_image_format_planes = dst_image_format.planes();
let dst_image_format_subsampled_extent = dst_image_format
.ycbcr_chroma_sampling()
.map_or(dst_image.extent(), |s| {
s.subsampled_extent(dst_image.extent())
});
if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
if !src_image
.format_features()
.intersects(FormatFeatures::TRANSFER_SRC)
{
return Err(Box::new(ValidationError {
context: "src_image.format_features()".into(),
problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01995"],
..Default::default()
}));
}
if !dst_image
.format_features()
.intersects(FormatFeatures::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
context: "dst_image.format_features()".into(),
problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01996"],
..Default::default()
}));
}
}
if src_image.samples() != dst_image.samples() {
return Err(Box::new(ValidationError {
problem: "`src_image.samples()` does not equal `dst_image.samples()`".into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-00136"],
..Default::default()
}));
}
if !matches!(
src_image_layout,
ImageLayout::TransferSrcOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "src_image_layout".into(),
problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImageLayout-01917"],
..Default::default()
}));
}
if !matches!(
dst_image_layout,
ImageLayout::TransferDstOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "dst_image_layout".into(),
problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImageLayout-01395"],
..Default::default()
}));
}
if src_image.image_type() != dst_image.image_type() {
if !(matches!(src_image.image_type(), ImageType::Dim2d | ImageType::Dim3d)
&& matches!(dst_image.image_type(), ImageType::Dim2d | ImageType::Dim3d))
{
return Err(Box::new(ValidationError {
problem: "`src_image.image_type()` does not equal `dst_image.image_type()`, \
but they are not both `ImageType::Dim2d` or `ImageType::Dim3d`"
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-07743"],
..Default::default()
}));
}
if !(device.api_version() >= Version::V1_1
|| device.enabled_extensions().khr_maintenance1)
{
return Err(Box::new(ValidationError {
problem: "`src_image.image_type()` does not equal `dst_image.image_type()`, \
and are both `ImageType::Dim2d` or `ImageType::Dim3d`"
.into(),
requires_one_of: RequiresOneOf(&[
RequiresAllOf(&[Requires::APIVersion(Version::V1_1)]),
RequiresAllOf(&[Requires::DeviceExtension("khr_maintenance1")]),
]),
vuids: &["VUID-VkCopyImageInfo2-apiVersion-07933"],
..Default::default()
}));
}
}
let is_same_image = src_image == dst_image;
let mut overlap_subresource_indices = None;
let mut overlap_extent_indices = None;
for (region_index, region) in regions.iter().enumerate() {
region
.validate(device)
.map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
let &ImageCopy {
ref src_subresource,
src_offset,
ref dst_subresource,
dst_offset,
extent,
_ne,
} = region;
if src_subresource.mip_level >= src_image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].src_subresource.mip_level` is not less than \
`src_image.mip_levels()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcSubresource-07967"],
..Default::default()
}));
}
let mut src_subresource_format = src_image_format;
let mut src_subresource_extent =
mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap();
if src_image_format_planes.is_empty() {
if !src_image_format_aspects.contains(src_subresource.aspects) {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].src_subresource.aspects` is not a subset of \
`src_image.format().aspects()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-aspectMask-00142"],
..Default::default()
}));
}
} else if src_image_format_planes.len() == 2 {
match src_subresource.aspects {
ImageAspects::PLANE_0 => {
src_subresource_format = src_image_format_planes[0];
}
ImageAspects::PLANE_1 => {
src_subresource_format = src_image_format_planes[1];
src_subresource_extent = src_image_format_subsampled_extent;
}
_ => {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is a multi-planar format with two planes, \
but `regions[{}].src_subresource.aspect` is not \
`ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageInfo2-srcImage-08713",
"VUID-VkCopyImageInfo2-aspectMask-00142",
],
..Default::default()
}));
}
}
} else if src_image_format_planes.len() == 3 {
match src_subresource.aspects {
ImageAspects::PLANE_0 => {
src_subresource_format = src_image_format_planes[0];
}
ImageAspects::PLANE_1 => {
src_subresource_format = src_image_format_planes[1];
src_subresource_extent = src_image_format_subsampled_extent;
}
ImageAspects::PLANE_2 => {
src_subresource_format = src_image_format_planes[2];
src_subresource_extent = src_image_format_subsampled_extent;
}
_ => {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is a multi-planar format with three planes, \
but `regions[{}].src_subresource.aspect` is not \
`ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \
`ImageAspects::PLANE_2`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageInfo2-srcImage-08713",
"VUID-VkCopyImageInfo2-aspectMask-00142",
],
..Default::default()
}));
}
}
}
match src_image.image_type() {
ImageType::Dim1d => {
if src_offset[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].src_offset[1]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-00146"],
..Default::default()
}));
}
if extent[1] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].extent[1]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-00146"],
..Default::default()
}));
}
if src_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].src_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01785"],
..Default::default()
}));
}
if extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01785"],
..Default::default()
}));
}
}
ImageType::Dim2d => {
if src_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].src_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01787"],
..Default::default()
}));
}
}
ImageType::Dim3d => {
if src_subresource.array_layers != (0..1) {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{}].src_subresource.array_layers` is not `0..1`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageInfo2-srcImage-04443",
"VUID-VkCopyImageInfo2-apiVersion-07932",
],
..Default::default()
}));
}
}
}
if src_subresource.array_layers.end > src_image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].src_subresource.array_layers.end` is not less than \
`src_image.array_layers()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcSubresource-07968"],
..Default::default()
}));
}
if src_offset[0] + extent[0] > src_subresource_extent[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is greater \
than coordinate 0 of the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcOffset-00144"],
..Default::default()
}));
}
if src_offset[1] + extent[1] > src_subresource_extent[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is greater \
than coordinate 1 of the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcOffset-00145"],
..Default::default()
}));
}
if src_offset[2] + extent[2] > src_subresource_extent[2] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is greater \
than coordinate 2 of the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcOffset-00147"],
..Default::default()
}));
}
let src_subresource_format_block_extent = src_subresource_format.block_extent();
if src_offset[0] % src_subresource_format_block_extent[0] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[0]` is not a multiple of coordinate 0 of the \
block extent of the format of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-pRegions-07278"],
..Default::default()
}));
}
if src_offset[1] % src_subresource_format_block_extent[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[1]` is not a multiple of coordinate 1 of the \
block extent of the format of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-pRegions-07279"],
..Default::default()
}));
}
if src_offset[2] % src_subresource_format_block_extent[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[2]` is not a multiple of coordinate 2 of the \
block extent of the format of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-pRegions-07280"],
..Default::default()
}));
}
if src_offset[0] + extent[0] != src_subresource_extent[0]
&& (src_offset[0] + extent[0]) % src_subresource_format_block_extent[0] != 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is not \
equal to the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`, but \
it is also not a multiple of coordinate 0 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01728"],
..Default::default()
}));
}
if src_offset[1] + extent[1] != src_subresource_extent[1]
&& (src_offset[1] + extent[1]) % src_subresource_format_block_extent[1] != 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is not \
equal to the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`, but \
it is also not a multiple of coordinate 1 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01729"],
..Default::default()
}));
}
if src_offset[2] + extent[2] != src_subresource_extent[2]
&& (src_offset[2] + extent[2]) % src_subresource_format_block_extent[2] != 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is not \
equal to the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`, but \
it is also not a multiple of coordinate 2 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01730"],
..Default::default()
}));
}
if !(src_subresource.aspects - ImageAspects::STENCIL).is_empty()
&& !src_image.usage().intersects(ImageUsage::TRANSFER_SRC)
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_subresource.aspects` contains aspects other than \
`ImageAspects::STENCIL`, but \
`src_image.usage()` does not contain `ImageUsage::TRANSFER_SRC`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-aspect-06662"],
..Default::default()
}));
}
if src_subresource.aspects.intersects(ImageAspects::STENCIL)
&& !src_image
.stencil_usage()
.unwrap_or(src_image.usage())
.intersects(ImageUsage::TRANSFER_SRC)
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_subresource.aspects` contains \
`ImageAspects::STENCIL`, but \
`src_image.stencil_usage()` does not contain `ImageUsage::TRANSFER_SRC`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-aspect-06664"],
..Default::default()
}));
}
if dst_subresource.mip_level >= dst_image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.mip_level` is not less than \
`dst_image.mip_levels()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstSubresource-07967"],
..Default::default()
}));
}
let mut dst_subresource_format = dst_image_format;
let mut dst_subresource_extent =
mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap();
if dst_image_format_planes.is_empty() {
if !dst_image_format_aspects.contains(dst_subresource.aspects) {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.aspects` is not a subset of \
`dst_image.format().aspects()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-aspectMask-00143"],
..Default::default()
}));
}
} else if dst_image_format_planes.len() == 2 {
match dst_subresource.aspects {
ImageAspects::PLANE_0 => {
dst_subresource_format = dst_image_format_planes[0];
}
ImageAspects::PLANE_1 => {
dst_subresource_format = dst_image_format_planes[1];
dst_subresource_extent = dst_image_format_subsampled_extent;
}
_ => {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.format()` is a multi-planar format with two planes, \
but `regions[{}].dst_subresource.aspect` is not \
`ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageInfo2-dstImage-08714",
"VUID-VkCopyImageInfo2-aspectMask-00143",
],
..Default::default()
}));
}
}
} else if dst_image_format_planes.len() == 3 {
match dst_subresource.aspects {
ImageAspects::PLANE_0 => {
dst_subresource_format = dst_image_format_planes[0];
}
ImageAspects::PLANE_1 => {
dst_subresource_format = dst_image_format_planes[1];
dst_subresource_extent = dst_image_format_subsampled_extent;
}
ImageAspects::PLANE_2 => {
dst_subresource_format = dst_image_format_planes[2];
dst_subresource_extent = dst_image_format_subsampled_extent;
}
_ => {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.format()` is a multi-planar format with three planes, \
but `regions[{}].dst_subresource.aspect` is not \
`ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \
`ImageAspects::PLANE_2`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageInfo2-dstImage-08714",
"VUID-VkCopyImageInfo2-aspectMask-00143",
],
..Default::default()
}));
}
}
}
match dst_image.image_type() {
ImageType::Dim1d => {
if dst_offset[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].dst_offset[1]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-00152"],
..Default::default()
}));
}
if extent[1] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].extent[1]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-00152"],
..Default::default()
}));
}
if dst_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].dst_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01786"],
..Default::default()
}));
}
if extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01786"],
..Default::default()
}));
}
}
ImageType::Dim2d => {
if dst_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].dst_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01788"],
..Default::default()
}));
}
}
ImageType::Dim3d => {
if dst_subresource.array_layers != (0..1) {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{}].dst_subresource.array_layers` is not `0..1`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageInfo2-dstImage-04444",
"VUID-VkCopyImageInfo2-apiVersion-07932",
],
..Default::default()
}));
}
}
}
if dst_subresource.array_layers.end > dst_image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.array_layers.end` is not less than \
`dst_image.array_layers()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstSubresource-07968"],
..Default::default()
}));
}
if dst_offset[0] + extent[0] > dst_subresource_extent[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is greater \
than coordinate 0 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstOffset-00150"],
..Default::default()
}));
}
if dst_offset[1] + extent[1] > dst_subresource_extent[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is greater \
than coordinate 1 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstOffset-00151"],
..Default::default()
}));
}
if dst_offset[2] + extent[2] > dst_subresource_extent[2] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is greater \
than coordinate 2 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstOffset-00153"],
..Default::default()
}));
}
let dst_subresource_format_block_extent = dst_subresource_format.block_extent();
if dst_offset[0] % dst_subresource_format_block_extent[0] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[0]` is not a multiple of coordinate 0 of the \
block extent of the format of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-pRegions-07281"],
..Default::default()
}));
}
if dst_offset[1] % dst_subresource_format_block_extent[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[1]` is not a multiple of coordinate 1 of the \
block extent of the format of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-pRegions-07282"],
..Default::default()
}));
}
if dst_offset[2] % dst_subresource_format_block_extent[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[2]` is not a multiple of coordinate 2 of the \
block extent of the format of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-pRegions-07283"],
..Default::default()
}));
}
if dst_offset[0] + extent[0] != dst_subresource_extent[0]
&& (dst_offset[0] + extent[0]) % dst_subresource_format_block_extent[0] != 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is not \
equal to the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`, but \
it is also not a multiple of coordinate 0 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01732"],
..Default::default()
}));
}
if dst_offset[1] + extent[1] != dst_subresource_extent[1]
&& (dst_offset[1] + extent[1]) % dst_subresource_format_block_extent[1] != 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is not \
equal to the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`, but \
it is also not a multiple of coordinate 1 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01733"],
..Default::default()
}));
}
if dst_offset[2] + extent[2] != dst_subresource_extent[2]
&& (dst_offset[2] + extent[2]) % dst_subresource_format_block_extent[2] != 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is not \
equal to the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`, but \
it is also not a multiple of coordinate 2 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01734"],
..Default::default()
}));
}
if !(dst_subresource.aspects - ImageAspects::STENCIL).is_empty()
&& !dst_image.usage().intersects(ImageUsage::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_subresource.aspects` contains aspects other than \
`ImageAspects::STENCIL`, but \
`dst_image.usage()` does not contain `ImageUsage::TRANSFER_DST`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-aspect-06663"],
..Default::default()
}));
}
if dst_subresource.aspects.intersects(ImageAspects::STENCIL)
&& !dst_image
.stencil_usage()
.unwrap_or(dst_image.usage())
.intersects(ImageUsage::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_subresource.aspects` contains \
`ImageAspects::STENCIL`, but \
`dst_image.stencil_usage()` does not contain `ImageUsage::TRANSFER_DST`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-aspect-06665"],
..Default::default()
}));
}
match (
src_image_format_planes.is_empty(),
dst_image_format_planes.is_empty(),
) {
(true, true) => {
if src_subresource.aspects != dst_subresource.aspects {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` and `dst_image.format()` are both not \
multi-planar formats, but \
`regions[{0}].src_subresource.aspects` does not equal \
`regions[{0}].dst_subresource.aspects`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01551"],
..Default::default()
}));
}
if src_image_format.block_size() != dst_image_format.block_size() {
return Err(Box::new(ValidationError {
problem: "`src_image.format()` and `dst_image.format()` are both not \
multi-planar formats, but \
`src_image.format().block_size()` does not equal \
`dst_image.format().block_size()`"
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01548"],
..Default::default()
}));
}
}
(false, true) => {
if dst_subresource.aspects != ImageAspects::COLOR {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is a multi-planar format, and \
`dst_image.format()` is not a multi-planar format, but \
`regions[{}].dst_subresource.aspects` is not \
`ImageAspects::COLOR`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01556"],
..Default::default()
}));
}
if src_subresource_format.block_size() != dst_image_format.block_size() {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is a multi-planar format, and \
`dst_image.format()` is not a multi-planar format, but \
the block size of the plane of `src_image.format()` selected by \
`regions[{}].src_subresource.aspects` does not equal \
`dst_image.format().block_size()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-None-01549"],
..Default::default()
}));
}
}
(true, false) => {
if src_subresource.aspects != ImageAspects::COLOR {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is not a multi-planar format, and \
`dst_image.format()` is a multi-planar format, but \
`regions[{}].src_subresource.aspects` is not \
`ImageAspects::COLOR`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01557"],
..Default::default()
}));
}
if src_image_format.block_size() != dst_subresource_format.block_size() {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is not a multi-planar format, and \
`dst_image.format()` is a multi-planar format, but \
`src_image.format().block_size()` does not equal \
the block size of the plane of `dst_image.format()` selected by \
`regions[{}].dst_subresource.aspects`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-None-01549"],
..Default::default()
}));
}
}
(false, false) => {
if src_subresource_format.block_size() != dst_subresource_format.block_size() {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` and `dst_image.format()` are both \
multi-planar formats, but \
the block size of the plane of `src_image.format()` selected by \
`regions[{0}].src_subresource.aspects` does not equal \
the block size of the plane of `dst_image.format()` selected by \
`regions[{0}].dst_subresource.aspects`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-None-01549"],
..Default::default()
}));
}
}
}
if src_image.image_type() == dst_image.image_type() {
if src_subresource.array_layers.len() != dst_subresource.array_layers.len() {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` equals `dst_image.image_type()`, but \
the length of `regions[{0}].src_subresource.array_layers` \
does not equal \
the length of `regions[{0}].dst_subresource.array_layers`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-07744"],
..Default::default()
}));
}
}
match (src_image.image_type(), dst_image.image_type()) {
(ImageType::Dim2d, ImageType::Dim2d) => {
if extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` and `dst_image.image_type()` are \
both `ImageType::Dim2d`, but `regions[{}].extent[2]` is not 1",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageInfo2-srcImage-01790",
"VUID-VkCopyImageInfo2-apiVersion-08969",
],
..Default::default()
}));
}
}
(ImageType::Dim2d, ImageType::Dim3d) => {
if extent[2] as usize != src_subresource.array_layers.len() {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim2d` and \
`dst_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{0}].extent[2]` does not equal the length of \
`regions[{0}].src_subresource.array_layers`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-srcImage-01791"],
..Default::default()
}));
}
}
(ImageType::Dim3d, ImageType::Dim2d) => {
if extent[2] as usize != dst_subresource.array_layers.len() {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim3d` and \
`dst_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{0}].extent[2]` does not equal the length of \
`regions[{0}].dst_subresource.array_layers`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-dstImage-01792"],
..Default::default()
}));
}
}
_ => (),
}
if is_same_image {
let src_region_index = region_index;
let src_subresource_axes = [
src_subresource.mip_level..src_subresource.mip_level + 1,
src_subresource.array_layers.start..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;
if src_image_format_aspects.intersects(ImageAspects::PLANE_0)
&& src_subresource.aspects != dst_subresource.aspects
{
continue;
}
let dst_subresource_axes = [
dst_subresource.mip_level..dst_subresource.mip_level + 1,
src_subresource.array_layers.start..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;
}
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],
];
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));
}
}
}
if let Some((src_region_index, dst_region_index)) = overlap_extent_indices {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \
overlaps with `regions[{1}].dst_subresource`, but \
the `src_offset` and `extent` of `regions[{0}]` overlaps with \
the `dst_offset` and `extent` of `regions[{1}]`",
src_region_index, dst_region_index
)
.into(),
vuids: &["VUID-VkCopyImageInfo2-pRegions-00124"],
..Default::default()
}));
}
if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices {
if src_image_layout != dst_image_layout {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \
overlaps with `regions[{1}].dst_subresource`, but \
`src_image_layout` does not equal `dst_image_layout`",
src_region_index, dst_region_index
)
.into(),
vuids: &[
"VUID-VkCopyImageInfo2-srcImageLayout-00128",
"VUID-VkCopyImageInfo2-dstImageLayout-00133",
],
..Default::default()
}));
}
}
Ok(())
}
pub(crate) fn to_vk2<'a>(
&self,
regions_vk: &'a [ash::vk::ImageCopy2<'static>],
) -> ash::vk::CopyImageInfo2<'a> {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
regions: _,
_ne: _,
} = self;
ash::vk::CopyImageInfo2::default()
.src_image(src_image.handle())
.src_image_layout(src_image_layout.into())
.dst_image(dst_image.handle())
.dst_image_layout(dst_image_layout.into())
.regions(regions_vk)
}
pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::ImageCopy2<'static>; 8]> {
self.regions.iter().map(ImageCopy::to_vk2).collect()
}
pub(crate) fn to_vk(&self) -> CopyImageInfoVk {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
regions: _,
_ne: _,
} = self;
CopyImageInfoVk {
src_image_vk: src_image.handle(),
src_image_layout_vk: src_image_layout.into(),
dst_image_vk: dst_image.handle(),
dst_image_layout_vk: dst_image_layout.into(),
}
}
pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::ImageCopy; 8]> {
self.regions.iter().map(ImageCopy::to_vk).collect()
}
}
pub(crate) struct CopyImageInfoVk {
pub(crate) src_image_vk: ash::vk::Image,
pub(crate) src_image_layout_vk: ash::vk::ImageLayout,
pub(crate) dst_image_vk: ash::vk::Image,
pub(crate) dst_image_layout_vk: ash::vk::ImageLayout,
}
#[derive(Clone, Debug)]
pub struct ImageCopy {
pub src_subresource: ImageSubresourceLayers,
pub src_offset: [u32; 3],
pub dst_subresource: ImageSubresourceLayers,
pub dst_offset: [u32; 3],
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(()),
}
}
}
impl ImageCopy {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_subresource,
src_offset: _,
ref dst_subresource,
dst_offset: _,
extent,
_ne,
} = self;
src_subresource
.validate(device)
.map_err(|err| err.add_context("src_subresource"))?;
dst_subresource
.validate(device)
.map_err(|err| err.add_context("dst_subresource"))?;
if device.api_version() < Version::V1_1 {
if src_subresource.aspects != dst_subresource.aspects
&& !device.enabled_extensions().khr_sampler_ycbcr_conversion
{
return Err(Box::new(ValidationError {
problem: "`src_subresource.aspects` does not equal `dst_subresource.aspects`"
.into(),
vuids: &["VUID-VkImageCopy2-apiVersion-07940"],
..Default::default()
}));
}
if src_subresource.array_layers.len() != dst_subresource.array_layers.len()
&& !device.enabled_extensions().khr_maintenance1
{
return Err(Box::new(ValidationError {
problem: "the length of `src_subresource.array_layers` does not equal \
the length of `dst_subresource.array_layers`"
.into(),
vuids: &["VUID-VkImageCopy2-extent-00140"],
..Default::default()
}));
}
}
if extent[0] == 0 {
return Err(Box::new(ValidationError {
context: "extent[0]".into(),
problem: "is zero".into(),
vuids: &["VUID-VkImageCopy2-extent-06668"],
..Default::default()
}));
}
if extent[1] == 0 {
return Err(Box::new(ValidationError {
context: "extent[1]".into(),
problem: "is zero".into(),
vuids: &["VUID-VkImageCopy2-extent-06669"],
..Default::default()
}));
}
if extent[2] == 0 {
return Err(Box::new(ValidationError {
context: "extent[2]".into(),
problem: "is zero".into(),
vuids: &["VUID-VkImageCopy2-extent-06670"],
..Default::default()
}));
}
Ok(())
}
pub(crate) fn to_vk2(&self) -> ash::vk::ImageCopy2<'static> {
let &Self {
ref src_subresource,
src_offset,
ref dst_subresource,
dst_offset,
extent,
_ne: _,
} = self;
ash::vk::ImageCopy2::default()
.src_subresource(src_subresource.to_vk())
.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.to_vk())
.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],
})
}
pub(crate) fn to_vk(&self) -> ash::vk::ImageCopy {
let &Self {
ref src_subresource,
src_offset,
ref dst_subresource,
dst_offset,
extent,
_ne: _,
} = self;
ash::vk::ImageCopy {
src_subresource: src_subresource.to_vk(),
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.to_vk(),
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],
},
}
}
}
#[derive(Clone, Debug)]
pub struct CopyBufferToImageInfo {
pub src_buffer: Subbuffer<[u8]>,
pub dst_image: Arc<Image>,
pub dst_image_layout: ImageLayout,
pub regions: SmallVec<[BufferImageCopy; 1]>,
pub _ne: crate::NonExhaustive,
}
impl CopyBufferToImageInfo {
#[inline]
pub fn buffer_image(src_buffer: Subbuffer<impl ?Sized>, dst_image: Arc<Image>) -> Self {
let region = BufferImageCopy {
image_subresource: dst_image.subresource_layers(),
image_extent: dst_image.extent(),
..Default::default()
};
Self {
src_buffer: src_buffer.into_bytes(),
dst_image,
dst_image_layout: ImageLayout::TransferDstOptimal,
regions: smallvec![region],
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_buffer,
ref dst_image,
dst_image_layout,
ref regions,
_ne: _,
} = self;
dst_image_layout.validate_device(device).map_err(|err| {
err.add_context("dst_image_layout")
.set_vuids(&["VUID-VkCopyBufferToImageInfo2-dstImageLayout-parameter"])
})?;
assert_eq!(device, src_buffer.device().as_ref());
assert_eq!(device, dst_image.device().as_ref());
let dst_image_format = dst_image.format();
let dst_image_format_aspects = dst_image_format.aspects();
let dst_image_format_planes = dst_image_format.planes();
let dst_image_format_subsampled_extent = dst_image_format
.ycbcr_chroma_sampling()
.map_or(dst_image.extent(), |s| {
s.subsampled_extent(dst_image.extent())
});
if !src_buffer
.buffer()
.usage()
.intersects(BufferUsage::TRANSFER_SRC)
{
return Err(Box::new(ValidationError {
context: "src_buffer.buffer().usage()".into(),
problem: "does not contain `BufferUsage::TRANSFER_SRC`".into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-srcBuffer-00174"],
..Default::default()
}));
}
if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) {
return Err(Box::new(ValidationError {
context: "dst_image.usage()".into(),
problem: "does not contain `ImageUsage::TRANSFER_DST`".into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-00177"],
..Default::default()
}));
}
if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
if !dst_image
.format_features()
.intersects(FormatFeatures::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
context: "dst_image.format_features()".into(),
problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-01997"],
..Default::default()
}));
}
}
if dst_image.samples() != SampleCount::Sample1 {
return Err(Box::new(ValidationError {
context: "dst_image.samples()".into(),
problem: "is not `SampleCount::Sample1`".into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07973"],
..Default::default()
}));
}
if !matches!(
dst_image_layout,
ImageLayout::TransferDstOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "dst_image_layout".into(),
problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImageLayout-01396"],
..Default::default()
}));
}
for (region_index, region) in regions.iter().enumerate() {
region
.validate(device)
.map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
let &BufferImageCopy {
buffer_offset,
buffer_row_length,
buffer_image_height,
ref image_subresource,
image_offset,
image_extent,
_ne: _,
} = region;
if image_subresource.mip_level >= dst_image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].image_subresource.mip_level` is not less than \
`dst_image.mip_levels()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-imageSubresource-01701"],
..Default::default()
}));
}
let mut image_subresource_format = dst_image_format;
let mut image_subresource_extent =
mip_level_extent(dst_image.extent(), image_subresource.mip_level).unwrap();
if dst_image_format_planes.is_empty() {
if !dst_image_format_aspects.contains(image_subresource.aspects) {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].image_subresource.aspects` is not a subset of \
`dst_image.format().aspects()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-aspectMask-00211"],
..Default::default()
}));
}
} else if dst_image_format_planes.len() == 2 {
match image_subresource.aspects {
ImageAspects::PLANE_0 => {
image_subresource_format = dst_image_format_planes[0];
}
ImageAspects::PLANE_1 => {
image_subresource_format = dst_image_format_planes[1];
image_subresource_extent = dst_image_format_subsampled_extent;
}
_ => {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.format()` is a multi-planar format with two planes, \
but `regions[{}].image_subresource.aspect` is not \
`ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyBufferToImageInfo2-dstImage-07981",
"VUID-VkCopyBufferToImageInfo2-aspectMask-00211",
],
..Default::default()
}));
}
}
} else if dst_image_format_planes.len() == 3 {
match image_subresource.aspects {
ImageAspects::PLANE_0 => {
image_subresource_format = dst_image_format_planes[0];
}
ImageAspects::PLANE_1 => {
image_subresource_format = dst_image_format_planes[1];
image_subresource_extent = dst_image_format_subsampled_extent;
}
ImageAspects::PLANE_2 => {
image_subresource_format = dst_image_format_planes[2];
image_subresource_extent = dst_image_format_subsampled_extent;
}
_ => {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.format()` is a multi-planar format with three planes, \
but `regions[{}].image_subresource.aspect` is not \
`ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \
`ImageAspects::PLANE_2`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyBufferToImageInfo2-dstImage-07982",
"VUID-VkCopyBufferToImageInfo2-aspectMask-00211",
],
..Default::default()
}));
}
}
}
match dst_image.image_type() {
ImageType::Dim1d => {
if image_offset[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].image_offset[1]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07979"],
..Default::default()
}));
}
if image_extent[1] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].image_extent[1]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07979"],
..Default::default()
}));
}
if image_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].image_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"],
..Default::default()
}));
}
if image_extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].image_extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"],
..Default::default()
}));
}
}
ImageType::Dim2d => {
if image_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].image_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"],
..Default::default()
}));
}
if image_extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].image_extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07980"],
..Default::default()
}));
}
}
ImageType::Dim3d => {
if image_subresource.array_layers != (0..1) {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{}].image_subresource.array_layers` is not `0..1`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07983"],
..Default::default()
}));
}
}
}
if image_subresource.array_layers.end > dst_image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.array_layers.end` is not less than \
`dst_image.array_layers()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-imageSubresource-07968"],
..Default::default()
}));
}
if image_offset[0] + image_extent[0] > image_subresource_extent[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is greater \
than coordinate 0 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-06223"],
..Default::default()
}));
}
if image_offset[1] + image_extent[1] > image_subresource_extent[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is greater \
than coordinate 1 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-06224"],
..Default::default()
}));
}
if image_offset[2] + image_extent[2] > image_subresource_extent[2] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is greater \
than coordinate 2 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-imageOffset-00200"],
..Default::default()
}));
}
let image_subresource_format_block_extent = image_subresource_format.block_extent();
if image_offset[0] % image_subresource_format_block_extent[0] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[0]` is not a multiple of coordinate 0 of the \
block extent of the format of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07274"],
..Default::default()
}));
}
if image_offset[1] % image_subresource_format_block_extent[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[1]` is not a multiple of coordinate 1 of the \
block extent of the format of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07275"],
..Default::default()
}));
}
if image_offset[2] % image_subresource_format_block_extent[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[2]` is not a multiple of coordinate 2 of the \
block extent of the format of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-07276"],
..Default::default()
}));
}
if image_offset[0] + image_extent[0] != image_subresource_extent[0]
&& (image_offset[0] + image_extent[0]) % image_subresource_format_block_extent[0]
!= 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is not \
equal to the extent of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`, but \
it is also not a multiple of coordinate 0 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00207"],
..Default::default()
}));
}
if image_offset[1] + image_extent[1] != image_subresource_extent[1]
&& (image_offset[1] + image_extent[1]) % image_subresource_format_block_extent[1]
!= 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is not \
equal to the extent of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`, but \
it is also not a multiple of coordinate 1 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00208"],
..Default::default()
}));
}
if image_offset[2] + image_extent[2] != image_subresource_extent[2]
&& (image_offset[2] + image_extent[2]) % image_subresource_format_block_extent[2]
!= 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is not \
equal to the extent of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`, but \
it is also not a multiple of coordinate 2 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-imageExtent-00209"],
..Default::default()
}));
}
let image_subresource_format_block_size = image_subresource_format.block_size();
if dst_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
if (src_buffer.offset() + buffer_offset) % 4 != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.format()` is a depth/stencil format, but \
`src_buffer.offset() + regions[{0}].buffer_offset` is not a \
multiple of 4",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-dstImage-07978"],
..Default::default()
}));
}
} else {
if (src_buffer.offset() + buffer_offset) % image_subresource_format_block_size != 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.format()` is not a depth/stencil format, but \
`src_buffer.offset() + regions[{0}].buffer_offset` is not a \
multiple of the block size of the format of the subresource of \
`dst_image` selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyBufferToImageInfo2-dstImage-07975",
"VUID-VkCopyBufferToImageInfo2-dstImage-07976",
],
..Default::default()
}));
}
}
if buffer_row_length % image_subresource_format_block_extent[0] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].buffer_row_length` is not a multiple of coordinate 0 of \
the block extent of the format of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203"],
..Default::default()
}));
}
if buffer_image_height % image_subresource_format_block_extent[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].buffer_image_height` is not a multiple of coordinate 1 of \
the block extent of the format of the subresource of `dst_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-bufferImageHeight-00204"],
..Default::default()
}));
}
if (buffer_row_length / image_subresource_format_block_extent[0]) as DeviceSize
* image_subresource_format_block_size
> 0x7FFFFFFF
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].buffer_row_length`, divided by the block size of the \
format of the subresource of `dst_image` selected by \
`regions[{0}].image_subresource`, and then multiplied by the block size \
of that subresource, is greater than 0x7FFFFFFF",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-bufferRowLength-00203"],
..Default::default()
}));
}
if buffer_offset + region.buffer_copy_size(image_subresource_format) > src_buffer.size()
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].buffer_offset` plus the number of bytes being copied \
is greater than `src_buffer.size()`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyBufferToImageInfo2-pRegions-00171"],
..Default::default()
}));
}
}
Ok(())
}
pub(crate) fn to_vk2<'a>(
&self,
regions_vk: &'a [ash::vk::BufferImageCopy2<'static>],
) -> ash::vk::CopyBufferToImageInfo2<'a> {
let &Self {
ref src_buffer,
ref dst_image,
dst_image_layout,
regions: _,
_ne: _,
} = self;
ash::vk::CopyBufferToImageInfo2::default()
.src_buffer(src_buffer.buffer().handle())
.dst_image(dst_image.handle())
.dst_image_layout(dst_image_layout.into())
.regions(regions_vk)
}
pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::BufferImageCopy2<'static>; 8]> {
let Self {
src_buffer,
regions,
..
} = self;
regions
.iter()
.map(|region| {
let mut region_vk = region.to_vk2();
region_vk.buffer_offset += src_buffer.offset();
region_vk
})
.collect()
}
pub(crate) fn to_vk(&self) -> CopyBufferToImageInfoVk {
let &Self {
ref src_buffer,
ref dst_image,
dst_image_layout,
regions: _,
_ne: _,
} = self;
CopyBufferToImageInfoVk {
src_buffer_vk: src_buffer.buffer().handle(),
dst_image_vk: dst_image.handle(),
dst_image_layout_vk: dst_image_layout.into(),
}
}
pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::BufferImageCopy; 8]> {
let Self {
src_buffer,
regions,
..
} = self;
regions
.iter()
.map(|region| {
let mut region_vk = region.to_vk();
region_vk.buffer_offset += src_buffer.offset();
region_vk
})
.collect()
}
}
pub(crate) struct CopyBufferToImageInfoVk {
pub(crate) src_buffer_vk: ash::vk::Buffer,
pub(crate) dst_image_vk: ash::vk::Image,
pub(crate) dst_image_layout_vk: ash::vk::ImageLayout,
}
#[derive(Clone, Debug)]
pub struct CopyImageToBufferInfo {
pub src_image: Arc<Image>,
pub src_image_layout: ImageLayout,
pub dst_buffer: Subbuffer<[u8]>,
pub regions: SmallVec<[BufferImageCopy; 1]>,
pub _ne: crate::NonExhaustive,
}
impl CopyImageToBufferInfo {
#[inline]
pub fn image_buffer(src_image: Arc<Image>, dst_buffer: Subbuffer<impl ?Sized>) -> Self {
let region = BufferImageCopy {
image_subresource: src_image.subresource_layers(),
image_extent: src_image.extent(),
..Default::default()
};
Self {
src_image,
src_image_layout: ImageLayout::TransferSrcOptimal,
dst_buffer: dst_buffer.into_bytes(),
regions: smallvec![region],
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_image,
src_image_layout,
ref dst_buffer,
ref regions,
_ne: _,
} = self;
src_image_layout.validate_device(device).map_err(|err| {
err.add_context("src_image_layout")
.set_vuids(&["VUID-VkCopyImageToBufferInfo2-srcImageLayout-parameter"])
})?;
assert_eq!(device, src_image.device().as_ref());
assert_eq!(device, dst_buffer.device().as_ref());
let src_image_format = src_image.format();
let src_image_format_aspects = src_image_format.aspects();
let src_image_format_planes = src_image_format.planes();
let src_image_format_subsampled_extent = src_image_format
.ycbcr_chroma_sampling()
.map_or(src_image.extent(), |s| {
s.subsampled_extent(src_image.extent())
});
if !dst_buffer
.buffer()
.usage()
.intersects(BufferUsage::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
context: "dst_buffer.buffer().usage()".into(),
problem: "does not contain `BufferUsage::TRANSFER_DST`".into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-dstBuffer-00191"],
..Default::default()
}));
}
if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) {
return Err(Box::new(ValidationError {
context: "src_image.usage()".into(),
problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-00186"],
..Default::default()
}));
}
if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
if !src_image
.format_features()
.intersects(FormatFeatures::TRANSFER_SRC)
{
return Err(Box::new(ValidationError {
context: "src_image.format_features()".into(),
problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-01998"],
..Default::default()
}));
}
}
if src_image.samples() != SampleCount::Sample1 {
return Err(Box::new(ValidationError {
context: "src_image.samples()".into(),
problem: "is not `SampleCount::Sample1`".into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07973"],
..Default::default()
}));
}
if !matches!(
src_image_layout,
ImageLayout::TransferSrcOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "src_image_layout".into(),
problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImageLayout-01397"],
..Default::default()
}));
}
for (region_index, region) in regions.iter().enumerate() {
region
.validate(device)
.map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
let &BufferImageCopy {
buffer_offset,
buffer_row_length,
buffer_image_height,
ref image_subresource,
image_offset,
image_extent,
_ne: _,
} = region;
if image_subresource.mip_level >= src_image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].image_subresource.mip_level` is not less than \
`src_image.mip_levels()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-imageSubresource-07967"],
..Default::default()
}));
}
let mut image_subresource_format = src_image_format;
let mut image_subresource_extent =
mip_level_extent(src_image.extent(), image_subresource.mip_level).unwrap();
if src_image_format_planes.is_empty() {
if !src_image_format_aspects.contains(image_subresource.aspects) {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].image_subresource.aspects` is not a subset of \
`src_image.format().aspects()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-aspectMask-00211"],
..Default::default()
}));
}
} else if src_image_format_planes.len() == 2 {
match image_subresource.aspects {
ImageAspects::PLANE_0 => {
image_subresource_format = src_image_format_planes[0];
}
ImageAspects::PLANE_1 => {
image_subresource_format = src_image_format_planes[1];
image_subresource_extent = src_image_format_subsampled_extent;
}
_ => {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is a multi-planar format with two planes, \
but `regions[{}].image_subresource.aspect` is not \
`ImageAspects::PLANE_0` or `ImageAspects::PLANE_1`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageToBufferInfo2-srcImage-07981",
"VUID-VkCopyImageToBufferInfo2-aspectMask-00211",
],
..Default::default()
}));
}
}
} else if src_image_format_planes.len() == 3 {
match image_subresource.aspects {
ImageAspects::PLANE_0 => {
image_subresource_format = src_image_format_planes[0];
}
ImageAspects::PLANE_1 => {
image_subresource_format = src_image_format_planes[1];
image_subresource_extent = src_image_format_subsampled_extent;
}
ImageAspects::PLANE_2 => {
image_subresource_format = src_image_format_planes[2];
image_subresource_extent = src_image_format_subsampled_extent;
}
_ => {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is a multi-planar format with three planes, \
but `regions[{}].image_subresource.aspect` is not \
`ImageAspects::PLANE_0`, `ImageAspects::PLANE_1` or \
`ImageAspects::PLANE_2`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageToBufferInfo2-srcImage-07982",
"VUID-VkCopyImageToBufferInfo2-aspectMask-00211",
],
..Default::default()
}));
}
}
}
match src_image.image_type() {
ImageType::Dim1d => {
if image_offset[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].image_offset[1]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07979"],
..Default::default()
}));
}
if image_extent[1] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].image_extent[1]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07979"],
..Default::default()
}));
}
if image_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].image_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"],
..Default::default()
}));
}
if image_extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].image_extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"],
..Default::default()
}));
}
}
ImageType::Dim2d => {
if image_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].image_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"],
..Default::default()
}));
}
if image_extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].image_extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07980"],
..Default::default()
}));
}
}
ImageType::Dim3d => {
if image_subresource.array_layers != (0..1) {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{}].image_subresource.array_layers` is not `0..1`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07983"],
..Default::default()
}));
}
}
}
if image_subresource.array_layers.end > src_image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.array_layers.end` is not less than \
`src_image.array_layers()`",
region_index
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-imageSubresource-07968"],
..Default::default()
}));
}
if image_offset[0] + image_extent[0] > image_subresource_extent[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is greater \
than coordinate 0 of the extent of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00197"],
..Default::default()
}));
}
if image_offset[1] + image_extent[1] > image_subresource_extent[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is greater \
than coordinate 1 of the extent of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00198"],
..Default::default()
}));
}
if image_offset[2] + image_extent[2] > image_subresource_extent[2] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is greater \
than coordinate 2 of the extent of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-imageOffset-00200"],
..Default::default()
}));
}
let image_subresource_format_block_extent = image_subresource_format.block_extent();
if image_offset[0] % image_subresource_format_block_extent[0] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[0]` is not a multiple of coordinate 0 of the \
block extent of the format of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07274"],
..Default::default()
}));
}
if image_offset[1] % image_subresource_format_block_extent[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[1]` is not a multiple of coordinate 1 of the \
block extent of the format of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07275"],
..Default::default()
}));
}
if image_offset[2] % image_subresource_format_block_extent[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[2]` is not a multiple of coordinate 2 of the \
block extent of the format of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07276"],
..Default::default()
}));
}
if image_offset[0] + image_extent[0] != image_subresource_extent[0]
&& (image_offset[0] + image_extent[0]) % image_subresource_format_block_extent[0]
!= 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[0] + regions[{0}].image_extent[0]` is not \
equal to the extent of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`, but \
it is also not a multiple of coordinate 0 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00207"],
..Default::default()
}));
}
if image_offset[1] + image_extent[1] != image_subresource_extent[1]
&& (image_offset[1] + image_extent[1]) % image_subresource_format_block_extent[1]
!= 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[1] + regions[{0}].image_extent[1]` is not \
equal to the extent of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`, but \
it is also not a multiple of coordinate 1 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00208"],
..Default::default()
}));
}
if image_offset[2] + image_extent[2] != image_subresource_extent[2]
&& (image_offset[2] + image_extent[2]) % image_subresource_format_block_extent[2]
!= 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].image_offset[2] + regions[{0}].image_extent[2]` is not \
equal to the extent of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`, but \
it is also not a multiple of coordinate 2 of the block extent of the \
format of that subresource",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-imageExtent-00209"],
..Default::default()
}));
}
let image_subresource_format_block_size = image_subresource_format.block_size();
if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
if (dst_buffer.offset() + buffer_offset) % 4 != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is a depth/stencil format, but \
`dst_buffer.offset() + regions[{0}].buffer_offset` is not a \
multiple of 4",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-srcImage-07978"],
..Default::default()
}));
}
} else {
if (dst_buffer.offset() + buffer_offset) % image_subresource_format_block_size != 0
{
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.format()` is not a depth/stencil format, but \
`dst_buffer.offset() + regions[{0}].buffer_offset` is not a \
multiple of the block size of the format of the subresource of \
`src_image` selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &[
"VUID-VkCopyImageToBufferInfo2-srcImage-07975",
"VUID-VkCopyImageToBufferInfo2-srcImage-07976",
],
..Default::default()
}));
}
}
if buffer_row_length % image_subresource_format_block_extent[0] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].buffer_row_length` is not a multiple of coordinate 0 of \
the block extent of the format of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-bufferRowLength-00203"],
..Default::default()
}));
}
if buffer_image_height % image_subresource_format_block_extent[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].buffer_image_height` is not a multiple of coordinate 1 of \
the block extent of the format of the subresource of `src_image` \
selected by `regions[{0}].image_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-bufferImageHeight-00204"],
..Default::default()
}));
}
if (buffer_row_length / image_subresource_format_block_extent[0]) as DeviceSize
* image_subresource_format_block_size
> 0x7FFFFFFF
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].buffer_row_length`, divided by the block size of the \
format of the subresource of `src_image` selected by \
`regions[{0}].image_subresource`, and then multiplied by the block size \
of that subresource, is greater than 0x7FFFFFFF",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-07277"],
..Default::default()
}));
}
if buffer_offset + region.buffer_copy_size(image_subresource_format) > dst_buffer.size()
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].buffer_offset` plus the number of bytes being copied \
is greater than `dst_buffer.size()`",
region_index,
)
.into(),
vuids: &["VUID-VkCopyImageToBufferInfo2-pRegions-00183"],
..Default::default()
}));
}
}
Ok(())
}
pub(crate) fn to_vk2<'a>(
&self,
regions_vk: &'a [ash::vk::BufferImageCopy2<'static>],
) -> ash::vk::CopyImageToBufferInfo2<'a> {
let &Self {
ref src_image,
src_image_layout,
ref dst_buffer,
regions: _,
_ne: _,
} = self;
ash::vk::CopyImageToBufferInfo2::default()
.src_image(src_image.handle())
.src_image_layout(src_image_layout.into())
.dst_buffer(dst_buffer.buffer().handle())
.regions(regions_vk)
}
pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::BufferImageCopy2<'static>; 8]> {
let Self {
dst_buffer,
regions,
..
} = self;
regions
.iter()
.map(|region| {
let mut region_vk = region.to_vk2();
region_vk.buffer_offset += dst_buffer.offset();
region_vk
})
.collect()
}
pub(crate) fn to_vk(&self) -> CopyImageToBufferInfoVk {
let &Self {
ref src_image,
src_image_layout,
ref dst_buffer,
regions: _,
_ne: _,
} = self;
CopyImageToBufferInfoVk {
src_image_vk: src_image.handle(),
src_image_layout_vk: src_image_layout.into(),
dst_buffer_vk: dst_buffer.buffer().handle(),
}
}
pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::BufferImageCopy; 8]> {
let Self {
dst_buffer,
regions,
..
} = self;
regions
.iter()
.map(|region| {
let mut region_vk = region.to_vk();
region_vk.buffer_offset += dst_buffer.offset();
region_vk
})
.collect()
}
}
pub(crate) struct CopyImageToBufferInfoVk {
pub(crate) src_image_vk: ash::vk::Image,
pub(crate) src_image_layout_vk: ash::vk::ImageLayout,
pub(crate) dst_buffer_vk: ash::vk::Buffer,
}
#[derive(Clone, Debug)]
pub struct BufferImageCopy {
pub buffer_offset: DeviceSize,
pub buffer_row_length: u32,
pub buffer_image_height: u32,
pub image_subresource: ImageSubresourceLayers,
pub image_offset: [u32; 3],
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 {
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];
}
let block_extent = format.block_extent();
buffer_row_length = buffer_row_length.div_ceil(block_extent[0]);
buffer_image_height = buffer_image_height.div_ceil(block_extent[1]);
for i in 0..3 {
image_extent[i] = image_extent[i].div_ceil(block_extent[i]);
}
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;
num_blocks * format.block_size()
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
buffer_offset: _,
buffer_row_length,
buffer_image_height,
ref image_subresource,
image_offset: _,
image_extent,
_ne: _,
} = self;
image_subresource
.validate(device)
.map_err(|err| err.add_context("image_subresource"))?;
if !(buffer_row_length == 0 || buffer_row_length >= image_extent[0]) {
return Err(Box::new(ValidationError {
problem: "`buffer_row_length` is not either zero, or greater than or equal to \
`image_extent[0]`"
.into(),
vuids: &["VUID-VkBufferImageCopy2-bufferRowLength-00195"],
..Default::default()
}));
}
if !(buffer_image_height == 0 || buffer_image_height >= image_extent[1]) {
return Err(Box::new(ValidationError {
problem: "`buffer_image_height` is not either zero, or greater than or equal to \
`image_extent[1]`"
.into(),
vuids: &["VUID-VkBufferImageCopy2-bufferImageHeight-00196"],
..Default::default()
}));
}
if image_subresource.aspects.count() != 1 {
return Err(Box::new(ValidationError {
context: "image_subresource.aspects".into(),
problem: "contains more than one aspect".into(),
vuids: &["VUID-VkBufferImageCopy2-aspectMask-00212"],
..Default::default()
}));
}
if image_extent[0] == 0 {
return Err(Box::new(ValidationError {
context: "image_extent[0]".into(),
problem: "is zero".into(),
vuids: &["VUID-VkBufferImageCopy2-imageExtent-06659"],
..Default::default()
}));
}
if image_extent[1] == 0 {
return Err(Box::new(ValidationError {
context: "image_extent[1]".into(),
problem: "is zero".into(),
vuids: &["VUID-VkBufferImageCopy2-imageExtent-06660"],
..Default::default()
}));
}
if image_extent[2] == 0 {
return Err(Box::new(ValidationError {
context: "image_extent[2]".into(),
problem: "is zero".into(),
vuids: &["VUID-VkBufferImageCopy2-imageExtent-06661"],
..Default::default()
}));
}
Ok(())
}
pub(crate) fn to_vk2(&self) -> ash::vk::BufferImageCopy2<'static> {
let &Self {
buffer_offset,
buffer_row_length,
buffer_image_height,
ref image_subresource,
image_offset,
image_extent,
_ne: _,
} = self;
ash::vk::BufferImageCopy2::default()
.buffer_offset(buffer_offset)
.buffer_row_length(buffer_row_length)
.buffer_image_height(buffer_image_height)
.image_subresource(image_subresource.to_vk())
.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],
})
}
pub(crate) fn to_vk(&self) -> ash::vk::BufferImageCopy {
let &Self {
buffer_offset,
buffer_row_length,
buffer_image_height,
ref image_subresource,
image_offset,
image_extent,
_ne: _,
} = self;
ash::vk::BufferImageCopy {
buffer_offset,
buffer_row_length,
buffer_image_height,
image_subresource: image_subresource.to_vk(),
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],
},
}
}
}
#[derive(Clone, Debug)]
pub struct BlitImageInfo {
pub src_image: Arc<Image>,
pub src_image_layout: ImageLayout,
pub dst_image: Arc<Image>,
pub dst_image_layout: ImageLayout,
pub regions: SmallVec<[ImageBlit; 1]>,
pub filter: Filter,
pub _ne: crate::NonExhaustive,
}
impl BlitImageInfo {
#[inline]
pub fn images(src_image: Arc<Image>, dst_image: Arc<Image>) -> Self {
let min_array_layers = src_image.array_layers().min(dst_image.array_layers());
let region = ImageBlit {
src_subresource: ImageSubresourceLayers {
array_layers: 0..min_array_layers,
..src_image.subresource_layers()
},
src_offsets: [[0; 3], src_image.extent()],
dst_subresource: ImageSubresourceLayers {
array_layers: 0..min_array_layers,
..dst_image.subresource_layers()
},
dst_offsets: [[0; 3], dst_image.extent()],
..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(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
ref regions,
filter,
_ne: _,
} = self;
src_image_layout.validate_device(device).map_err(|err| {
err.add_context("src_image_layout")
.set_vuids(&["VUID-VkBlitImageInfo2-srcImageLayout-parameter"])
})?;
dst_image_layout.validate_device(device).map_err(|err| {
err.add_context("dst_image_layout")
.set_vuids(&["VUID-VkBlitImageInfo2-dstImageLayout-parameter"])
})?;
filter.validate_device(device).map_err(|err| {
err.add_context("filter")
.set_vuids(&["VUID-VkBlitImageInfo2-filter-parameter"])
})?;
assert_eq!(device, src_image.device().as_ref());
assert_eq!(device, dst_image.device().as_ref());
let src_image_format = src_image.format();
let src_image_format_aspects = src_image_format.aspects();
let dst_image_format = dst_image.format();
let dst_image_format_aspects = dst_image_format.aspects();
if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) {
return Err(Box::new(ValidationError {
context: "src_image.usage()".into(),
problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00219"],
..Default::default()
}));
}
if !src_image
.format_features()
.intersects(FormatFeatures::BLIT_SRC)
{
return Err(Box::new(ValidationError {
context: "src_image.format_features()".into(),
problem: "does not contain `FormatFeatures::BLIT_SRC`".into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-01999"],
..Default::default()
}));
}
if src_image_format.ycbcr_chroma_sampling().is_some() {
return Err(Box::new(ValidationError {
context: "src_image.format()".into(),
problem: "is a YCbCr format".into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-06421"],
..Default::default()
}));
}
if src_image.samples() != SampleCount::Sample1 {
return Err(Box::new(ValidationError {
context: "src_image.samples()".into(),
problem: "is not `SampleCount::Sample1`".into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00233"],
..Default::default()
}));
}
if !matches!(
src_image_layout,
ImageLayout::TransferSrcOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "src_image_layout".into(),
problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImageLayout-01398"],
..Default::default()
}));
}
if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) {
return Err(Box::new(ValidationError {
context: "dst_image.usage()".into(),
problem: "does not contain `ImageUsage::TRANSFER_DST`".into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-00224"],
..Default::default()
}));
}
if !dst_image
.format_features()
.intersects(FormatFeatures::BLIT_DST)
{
return Err(Box::new(ValidationError {
context: "dst_image.format_features()".into(),
problem: "does not contain `FormatFeatures::BLIT_DST`".into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-02000"],
..Default::default()
}));
}
if dst_image_format.ycbcr_chroma_sampling().is_some() {
return Err(Box::new(ValidationError {
context: "dst_image.format()".into(),
problem: "is a YCbCr format".into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-06422"],
..Default::default()
}));
}
if dst_image.samples() != SampleCount::Sample1 {
return Err(Box::new(ValidationError {
context: "dst_image.samples()".into(),
problem: "is not `SampleCount::Sample1`".into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-00234"],
..Default::default()
}));
}
if !matches!(
dst_image_layout,
ImageLayout::TransferDstOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "dst_image_layout".into(),
problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstImageLayout-01399"],
..Default::default()
}));
}
if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
|| dst_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
{
if src_image_format != dst_image_format {
return Err(Box::new(ValidationError {
problem: "one of `src_image.format()` or `dst_image.format()` is a \
depth/stencil format, but they are not equal"
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00231"],
..Default::default()
}));
}
} else {
if src_image_format
.numeric_format_color()
.unwrap()
.numeric_type()
!= dst_image_format
.numeric_format_color()
.unwrap()
.numeric_type()
{
return Err(Box::new(ValidationError {
problem: "neither `src_image.format()` nor `dst_image.format()` is a \
depth/stencil format, but their numeric types are not equal"
.into(),
vuids: &[
"VUID-VkBlitImageInfo2-srcImage-00229",
"VUID-VkBlitImageInfo2-srcImage-00230",
],
..Default::default()
}));
}
}
if src_image_format_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL)
&& filter != Filter::Nearest
{
return Err(Box::new(ValidationError {
problem: "`src_image.format()` is a depth/stencil format, but \
`filter` is not `Filter::Nearest`"
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00232"],
..Default::default()
}));
}
match filter {
Filter::Nearest => (),
Filter::Linear => {
if !src_image
.format_features()
.intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR)
{
return Err(Box::new(ValidationError {
problem: "`filter` is `Filter::Linear`, but \
`src_image.format_features()` do not contain \
`FormatFeatures::SAMPLED_IMAGE_FILTER_LINEAR`"
.into(),
vuids: &["VUID-VkBlitImageInfo2-filter-02001"],
..Default::default()
}));
}
}
Filter::Cubic => {
if !src_image
.format_features()
.intersects(FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC)
{
return Err(Box::new(ValidationError {
problem: "`filter` is `Filter::Cubic`, but \
`src_image.format_features()` do not contain \
`FormatFeatures::SAMPLED_IMAGE_FILTER_CUBIC`"
.into(),
vuids: &["VUID-VkBlitImageInfo2-filter-02002"],
..Default::default()
}));
}
if src_image.image_type() != ImageType::Dim2d {
return Err(Box::new(ValidationError {
problem: "`filter` is `Filter::Cubic`, but \
`src_image.image_type()` is not `ImageType::Dim2d`"
.into(),
vuids: &["VUID-VkBlitImageInfo2-filter-00237"],
..Default::default()
}));
}
}
}
let is_same_image = src_image == dst_image;
let mut overlap_subresource_indices = None;
let mut overlap_extent_indices = None;
for (region_index, region) in regions.iter().enumerate() {
region
.validate(device)
.map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
let &ImageBlit {
ref src_subresource,
src_offsets,
ref dst_subresource,
dst_offsets,
_ne: _,
} = region;
if src_subresource.mip_level >= src_image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].src_subresource.mip_level` is not less than \
`src_image.mip_levels()`",
region_index
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01705"],
..Default::default()
}));
}
let src_subresource_extent =
mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap();
if !src_image_format_aspects.contains(src_subresource.aspects) {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].src_subresource.aspects` is not a subset of \
`src_image.format().aspects()`",
region_index
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-aspectMask-00241"],
..Default::default()
}));
}
match src_image.image_type() {
ImageType::Dim1d => {
if src_offsets[0][1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].src_offsets[0][1]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00245"],
..Default::default()
}));
}
if src_offsets[1][1] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].src_offsets[1][1]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00245"],
..Default::default()
}));
}
if src_offsets[0][2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].src_offsets[0][2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"],
..Default::default()
}));
}
if src_offsets[1][2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].src_offsets[1][2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"],
..Default::default()
}));
}
}
ImageType::Dim2d => {
if src_offsets[0][2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].src_offsets[0][2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"],
..Default::default()
}));
}
if src_offsets[1][2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].src_offsets[1][2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00247"],
..Default::default()
}));
}
}
ImageType::Dim3d => {
if src_subresource.array_layers != (0..1) {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{}].src_subresource.array_layers` is not `0..1`",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00240"],
..Default::default()
}));
}
}
}
if src_subresource.array_layers.end > src_image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].src_subresource.array_layers.end` is not less than \
`src_image.array_layers()`",
region_index
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01707"],
..Default::default()
}));
}
let src_offsets_max = [
max(src_offsets[0][0], src_offsets[1][0]),
max(src_offsets[0][1], src_offsets[1][1]),
max(src_offsets[0][2], src_offsets[1][2]),
];
if src_offsets_max[0] > src_subresource_extent[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`max(regions[{0}].src_offsets[0][0], regions[{0}].src_offsets[1][0])` is \
greater than coordinate 0 of the extent of the subresource of \
`src_image` selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcOffset-00243"],
..Default::default()
}));
}
if src_offsets_max[1] > src_subresource_extent[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`max(regions[{0}].src_offsets[0][1], regions[{0}].src_offsets[1][1])` is \
greater than coordinate 1 of the extent of the subresource of \
`src_image` selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcOffset-00244"],
..Default::default()
}));
}
if src_offsets_max[2] > src_subresource_extent[2] {
return Err(Box::new(ValidationError {
problem: format!(
"`max(regions[{0}].src_offsets[0][2], regions[{0}].src_offsets[1][2])` is \
greater than coordinate 2 of the extent of the subresource of \
`src_image` selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcOffset-00246"],
..Default::default()
}));
}
if dst_subresource.mip_level >= dst_image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.mip_level` is not less than \
`dst_image.mip_levels()`",
region_index
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01705"],
..Default::default()
}));
}
let dst_subresource_extent =
mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap();
if !dst_image_format_aspects.contains(dst_subresource.aspects) {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.aspects` is not a subset of \
`dst_image.format().aspects()`",
region_index
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-aspectMask-00242"],
..Default::default()
}));
}
match dst_image.image_type() {
ImageType::Dim1d => {
if dst_offsets[0][1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].dst_offsets[0][1]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-00250"],
..Default::default()
}));
}
if dst_offsets[1][1] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].dst_offsets[1][1]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-00250"],
..Default::default()
}));
}
if dst_offsets[0][2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].dst_offsets[0][2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"],
..Default::default()
}));
}
if dst_offsets[1][2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].dst_offsets[1][2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"],
..Default::default()
}));
}
}
ImageType::Dim2d => {
if dst_offsets[0][2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].dst_offsets[0][2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"],
..Default::default()
}));
}
if dst_offsets[1][2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].dst_offsets[1][2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstImage-00252"],
..Default::default()
}));
}
}
ImageType::Dim3d => {
if dst_subresource.array_layers != (0..1) {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{}].dst_subresource.array_layers` is not `0..1`",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcImage-00240"],
..Default::default()
}));
}
}
}
if dst_subresource.array_layers.end > dst_image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.array_layers.end` is not less than \
`dst_image.array_layers()`",
region_index
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-srcSubresource-01707"],
..Default::default()
}));
}
let dst_offsets_max = [
max(dst_offsets[0][0], dst_offsets[1][0]),
max(dst_offsets[0][1], dst_offsets[1][1]),
max(dst_offsets[0][2], dst_offsets[1][2]),
];
if dst_offsets_max[0] > dst_subresource_extent[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`max(regions[{0}].dst_offsets[0][0], regions[{0}].dst_offsets[1][0])` is \
greater than coordinate 0 of the extent of the subresource of \
`dst_image` selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstOffset-00248"],
..Default::default()
}));
}
if dst_offsets_max[1] > dst_subresource_extent[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`max(regions[{0}].dst_offsets[0][1], regions[{0}].dst_offsets[1][1])` is \
greater than coordinate 1 of the extent of the subresource of \
`dst_image` selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstOffset-00249"],
..Default::default()
}));
}
if dst_offsets_max[2] > dst_subresource_extent[2] {
return Err(Box::new(ValidationError {
problem: format!(
"`max(regions[{0}].dst_offsets[0][2], regions[{0}].dst_offsets[1][2])` is \
greater than coordinate 2 of the extent of the subresource of \
`dst_image` selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-dstOffset-00251"],
..Default::default()
}));
}
if is_same_image {
let src_region_index = region_index;
let src_subresource_axes = [
src_subresource.mip_level..src_subresource.mip_level + 1,
src_subresource.array_layers.start..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_subresource.mip_level..dst_subresource.mip_level + 1,
src_subresource.array_layers.start..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;
}
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;
}
overlap_extent_indices = Some((src_region_index, dst_region_index));
}
}
}
if let Some((src_region_index, dst_region_index)) = overlap_extent_indices {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \
overlaps with `regions[{1}].dst_subresource`, but \
the `src_offsets` of `regions[{0}]` overlaps with \
the `dst_offsets` of `regions[{1}]`",
src_region_index, dst_region_index
)
.into(),
vuids: &["VUID-VkBlitImageInfo2-pRegions-00217"],
..Default::default()
}));
}
if let Some((src_region_index, dst_region_index)) = overlap_subresource_indices {
if src_image_layout != dst_image_layout {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image` is equal to `dst_image`, and `regions[{0}].src_subresource` \
overlaps with `regions[{1}].dst_subresource`, but \
`src_image_layout` does not equal `dst_image_layout`",
src_region_index, dst_region_index
)
.into(),
vuids: &[
"VUID-VkBlitImageInfo2-srcImageLayout-00221",
"VUID-VkBlitImageInfo2-dstImageLayout-00226",
],
..Default::default()
}));
}
}
Ok(())
}
pub(crate) fn to_vk2<'a>(
&self,
regions_vk: &'a [ash::vk::ImageBlit2<'static>],
) -> ash::vk::BlitImageInfo2<'a> {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
regions: _,
filter,
_ne: _,
} = self;
ash::vk::BlitImageInfo2::default()
.src_image(src_image.handle())
.src_image_layout(src_image_layout.into())
.dst_image(dst_image.handle())
.dst_image_layout(dst_image_layout.into())
.regions(regions_vk)
.filter(filter.into())
}
pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::ImageBlit2<'static>; 8]> {
self.regions.iter().map(ImageBlit::to_vk2).collect()
}
pub(crate) fn to_vk(&self) -> BlitImageInfoVk {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
regions: _,
filter,
_ne: _,
} = self;
BlitImageInfoVk {
src_image_vk: src_image.handle(),
src_image_layout_vk: src_image_layout.into(),
dst_image_vk: dst_image.handle(),
dst_image_layout_vk: dst_image_layout.into(),
filter_vk: filter.into(),
}
}
pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::ImageBlit; 8]> {
self.regions.iter().map(ImageBlit::to_vk).collect()
}
}
pub(crate) struct BlitImageInfoVk {
pub(crate) src_image_vk: ash::vk::Image,
pub(crate) src_image_layout_vk: ash::vk::ImageLayout,
pub(crate) dst_image_vk: ash::vk::Image,
pub(crate) dst_image_layout_vk: ash::vk::ImageLayout,
pub(crate) filter_vk: ash::vk::Filter,
}
#[derive(Clone, Debug)]
pub struct ImageBlit {
pub src_subresource: ImageSubresourceLayers,
pub src_offsets: [[u32; 3]; 2],
pub dst_subresource: ImageSubresourceLayers,
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(()),
}
}
}
impl ImageBlit {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_subresource,
src_offsets: _,
ref dst_subresource,
dst_offsets: _,
_ne: _,
} = self;
src_subresource
.validate(device)
.map_err(|err| err.add_context("src_subresource"))?;
dst_subresource
.validate(device)
.map_err(|err| err.add_context("dst_subresource"))?;
if src_subresource.aspects != dst_subresource.aspects {
return Err(Box::new(ValidationError {
problem: "`src_subresource.aspects` does not equal `dst_subresource.aspects`"
.into(),
vuids: &["VUID-VkImageBlit2-aspectMask-00238"],
..Default::default()
}));
}
if src_subresource.array_layers.len() != dst_subresource.array_layers.len() {
return Err(Box::new(ValidationError {
problem: "the length of `src_subresource.array_layers` does not equal \
the length of `dst_subresource.array_layers`"
.into(),
vuids: &["VUID-VkImageBlit2-layerCount-00239"],
..Default::default()
}));
}
Ok(())
}
pub(crate) fn to_vk2(&self) -> ash::vk::ImageBlit2<'static> {
let &Self {
ref src_subresource,
src_offsets,
ref dst_subresource,
dst_offsets,
_ne: _,
} = self;
ash::vk::ImageBlit2::default()
.src_subresource(src_subresource.to_vk())
.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.to_vk())
.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,
},
])
}
pub(crate) fn to_vk(&self) -> ash::vk::ImageBlit {
let &Self {
ref src_subresource,
src_offsets,
ref dst_subresource,
dst_offsets,
_ne: _,
} = self;
ash::vk::ImageBlit {
src_subresource: src_subresource.to_vk(),
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.to_vk(),
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,
},
],
}
}
}
#[derive(Clone, Debug)]
pub struct ResolveImageInfo {
pub src_image: Arc<Image>,
pub src_image_layout: ImageLayout,
pub dst_image: Arc<Image>,
pub dst_image_layout: ImageLayout,
pub regions: SmallVec<[ImageResolve; 1]>,
pub _ne: crate::NonExhaustive,
}
impl ResolveImageInfo {
#[inline]
pub fn images(src_image: Arc<Image>, dst_image: Arc<Image>) -> Self {
let min_array_layers = src_image.array_layers().min(dst_image.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.extent();
let dst_extent = dst_image.extent();
[
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(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
ref regions,
_ne: _,
} = self;
src_image_layout.validate_device(device).map_err(|err| {
err.add_context("src_image_layout")
.set_vuids(&["VUID-VkResolveImageInfo2-srcImageLayout-parameter"])
})?;
dst_image_layout.validate_device(device).map_err(|err| {
err.add_context("dst_image_layout")
.set_vuids(&["VUID-VkResolveImageInfo2-dstImageLayout-parameter"])
})?;
assert_eq!(device, src_image.device().as_ref());
assert_eq!(device, dst_image.device().as_ref());
let src_image_format = src_image.format();
let dst_image_format = dst_image.format();
if src_image.samples() == SampleCount::Sample1 {
return Err(Box::new(ValidationError {
context: "src_image.samples()".into(),
problem: "is `SampleCount::Sample1`".into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-00257"],
..Default::default()
}));
}
if !src_image.usage().intersects(ImageUsage::TRANSFER_SRC) {
return Err(Box::new(ValidationError {
context: "src_image.usage()".into(),
problem: "does not contain `ImageUsage::TRANSFER_SRC`".into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-06762"],
..Default::default()
}));
}
if !src_image
.format_features()
.intersects(FormatFeatures::TRANSFER_SRC)
{
return Err(Box::new(ValidationError {
context: "src_image.format_features()".into(),
problem: "does not contain `FormatFeatures::TRANSFER_SRC`".into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-06763"],
..Default::default()
}));
}
if !matches!(
src_image_layout,
ImageLayout::TransferSrcOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "src_image_layout".into(),
problem: "is not `ImageLayout::TransferSrcOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImageLayout-01400"],
..Default::default()
}));
}
if dst_image.samples() != SampleCount::Sample1 {
return Err(Box::new(ValidationError {
context: "dst_image.samples()".into(),
problem: "is not `SampleCount::Sample1`".into(),
vuids: &["VUID-VkResolveImageInfo2-dstImage-00259"],
..Default::default()
}));
}
if !dst_image.usage().intersects(ImageUsage::TRANSFER_DST) {
return Err(Box::new(ValidationError {
context: "dst_image.usage()".into(),
problem: "does not contain `ImageUsage::TRANSFER_DST`".into(),
vuids: &["VUID-VkResolveImageInfo2-dstImage-06764"],
..Default::default()
}));
}
if !dst_image
.format_features()
.contains(FormatFeatures::TRANSFER_DST | FormatFeatures::COLOR_ATTACHMENT)
{
return Err(Box::new(ValidationError {
context: "dst_image.format_features()".into(),
problem: "does not contain both `FormatFeatures::TRANSFER_DST` and \
`FormatFeatures::COLOR_ATTACHMENT`"
.into(),
vuids: &[
"VUID-VkResolveImageInfo2-dstImage-06765",
"VUID-VkResolveImageInfo2-dstImage-02003",
],
..Default::default()
}));
}
if device.enabled_features().linear_color_attachment
&& dst_image.tiling() == ImageTiling::Linear
&& !dst_image
.format_features()
.contains(FormatFeatures::LINEAR_COLOR_ATTACHMENT)
{
return Err(Box::new(ValidationError {
problem: "the `linear_color_attachment` feature is enabled on the device, and \
`dst_image.tiling()` is `ImageTiling::Linear`, but \
`dst_image.format_features()` does not contain \
`FormatFeatures::LINEAR_COLOR_ATTACHMENT`"
.into(),
vuids: &["VUID-VkResolveImageInfo2-linearColorAttachment-06519"],
..Default::default()
}));
}
if !matches!(
dst_image_layout,
ImageLayout::TransferDstOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "dst_image_layout".into(),
problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstImageLayout-01401"],
..Default::default()
}));
}
if src_image_format != dst_image_format {
return Err(Box::new(ValidationError {
problem: "`src_image.format()` does not equal `dst_image.format()`".into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-01386"],
..Default::default()
}));
}
for (region_index, region) in regions.iter().enumerate() {
region
.validate(device)
.map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
let &ImageResolve {
ref src_subresource,
src_offset,
ref dst_subresource,
dst_offset,
extent,
_ne: _,
} = region;
if src_subresource.mip_level >= src_image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].src_subresource.mip_level` is not less than \
`src_image.mip_levels()`",
region_index
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcSubresource-01709"],
..Default::default()
}));
}
let src_subresource_extent =
mip_level_extent(src_image.extent(), src_subresource.mip_level).unwrap();
match src_image.image_type() {
ImageType::Dim1d => {
if src_offset[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].src_offset[1]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-00271"],
..Default::default()
}));
}
if extent[1] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].extent[1]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-00271"],
..Default::default()
}));
}
if src_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].src_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"],
..Default::default()
}));
}
if extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"],
..Default::default()
}));
}
}
ImageType::Dim2d => {
if src_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].src_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"],
..Default::default()
}));
}
if extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-00273"],
..Default::default()
}));
}
}
ImageType::Dim3d => {
if src_subresource.array_layers != (0..1) {
return Err(Box::new(ValidationError {
problem: format!(
"`src_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{}].src_subresource.array_layers` is not `0..1`",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-04446"],
..Default::default()
}));
}
}
}
if src_subresource.array_layers.end > src_image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].src_subresource.array_layers.end` is not less than \
`src_image.array_layers()`",
region_index
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcSubresource-01711"],
..Default::default()
}));
}
if src_offset[0] + extent[0] > src_subresource_extent[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[0] + regions[{0}].extent[0]` is greater \
than coordinate 0 of the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcOffset-00269"],
..Default::default()
}));
}
if src_offset[1] + extent[1] > src_subresource_extent[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[1] + regions[{0}].extent[1]` is greater \
than coordinate 1 of the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcOffset-00270"],
..Default::default()
}));
}
if src_offset[2] + extent[2] > src_subresource_extent[2] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].src_offset[2] + regions[{0}].extent[2]` is greater \
than coordinate 2 of the extent of the subresource of `src_image` \
selected by `regions[{0}].src_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcOffset-00272"],
..Default::default()
}));
}
if dst_subresource.mip_level >= dst_image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.mip_level` is not less than \
`dst_image.mip_levels()`",
region_index
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstSubresource-01710"],
..Default::default()
}));
}
let dst_subresource_extent =
mip_level_extent(dst_image.extent(), dst_subresource.mip_level).unwrap();
match dst_image.image_type() {
ImageType::Dim1d => {
if dst_offset[1] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].dst_offset[1]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstImage-00276"],
..Default::default()
}));
}
if extent[1] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].extent[1]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstImage-00276"],
..Default::default()
}));
}
if dst_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].dst_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"],
..Default::default()
}));
}
if extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim1d`, but \
`regions[{}].extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"],
..Default::default()
}));
}
}
ImageType::Dim2d => {
if dst_offset[2] != 0 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].dst_offset[2]` is not 0",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"],
..Default::default()
}));
}
if extent[2] != 1 {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim2d`, but \
`regions[{}].extent[2]` is not 1",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstImage-00278"],
..Default::default()
}));
}
}
ImageType::Dim3d => {
if dst_subresource.array_layers != (0..1) {
return Err(Box::new(ValidationError {
problem: format!(
"`dst_image.image_type()` is `ImageType::Dim3d`, but \
`regions[{}].dst_subresource.array_layers` is not `0..1`",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-srcImage-04447"],
..Default::default()
}));
}
}
}
if dst_subresource.array_layers.end > dst_image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].dst_subresource.array_layers.end` is not less than \
`dst_image.array_layers()`",
region_index
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstSubresource-01712"],
..Default::default()
}));
}
if dst_offset[0] + extent[0] > dst_subresource_extent[0] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[0] + regions[{0}].extent[0]` is greater \
than coordinate 0 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstOffset-00274"],
..Default::default()
}));
}
if dst_offset[1] + extent[1] > dst_subresource_extent[1] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[1] + regions[{0}].extent[1]` is greater \
than coordinate 1 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstOffset-00275"],
..Default::default()
}));
}
if dst_offset[2] + extent[2] > dst_subresource_extent[2] {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{0}].dst_offset[2] + regions[{0}].extent[2]` is greater \
than coordinate 2 of the extent of the subresource of `dst_image` \
selected by `regions[{0}].dst_subresource`",
region_index,
)
.into(),
vuids: &["VUID-VkResolveImageInfo2-dstOffset-00277"],
..Default::default()
}));
}
}
Ok(())
}
pub(crate) fn to_vk2<'a>(
&self,
regions_vk: &'a [ash::vk::ImageResolve2<'static>],
) -> ash::vk::ResolveImageInfo2<'a> {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
regions: _,
_ne: _,
} = self;
ash::vk::ResolveImageInfo2::default()
.src_image(src_image.handle())
.src_image_layout(src_image_layout.into())
.dst_image(dst_image.handle())
.dst_image_layout(dst_image_layout.into())
.regions(regions_vk)
}
pub(crate) fn to_vk2_regions(&self) -> SmallVec<[ash::vk::ImageResolve2<'static>; 8]> {
self.regions.iter().map(ImageResolve::to_vk2).collect()
}
pub(crate) fn to_vk(&self) -> ResolveImageInfoVk {
let &Self {
ref src_image,
src_image_layout,
ref dst_image,
dst_image_layout,
regions: _,
_ne: _,
} = self;
ResolveImageInfoVk {
src_image_vk: src_image.handle(),
src_image_layout_vk: src_image_layout.into(),
dst_image_vk: dst_image.handle(),
dst_image_layout_vk: dst_image_layout.into(),
}
}
pub(crate) fn to_vk_regions(&self) -> SmallVec<[ash::vk::ImageResolve; 8]> {
self.regions.iter().map(ImageResolve::to_vk).collect()
}
}
pub(crate) struct ResolveImageInfoVk {
pub(crate) src_image_vk: ash::vk::Image,
pub(crate) src_image_layout_vk: ash::vk::ImageLayout,
pub(crate) dst_image_vk: ash::vk::Image,
pub(crate) dst_image_layout_vk: ash::vk::ImageLayout,
}
#[derive(Clone, Debug)]
pub struct ImageResolve {
pub src_subresource: ImageSubresourceLayers,
pub src_offset: [u32; 3],
pub dst_subresource: ImageSubresourceLayers,
pub dst_offset: [u32; 3],
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(()),
}
}
}
impl ImageResolve {
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref src_subresource,
src_offset: _,
ref dst_subresource,
dst_offset: _,
extent: _,
_ne: _,
} = self;
src_subresource
.validate(device)
.map_err(|err| err.add_context("src_subresource"))?;
dst_subresource
.validate(device)
.map_err(|err| err.add_context("dst_subresource"))?;
if src_subresource.aspects != ImageAspects::COLOR {
return Err(Box::new(ValidationError {
problem: "`src_subresource.aspects` is not `ImageAspects::COLOR`".into(),
vuids: &["VUID-VkImageResolve2-aspectMask-00266"],
..Default::default()
}));
}
if dst_subresource.aspects != ImageAspects::COLOR {
return Err(Box::new(ValidationError {
problem: "`dst_subresource.aspects` is not `ImageAspects::COLOR`".into(),
vuids: &["VUID-VkImageResolve2-aspectMask-00266"],
..Default::default()
}));
}
if src_subresource.array_layers.len() != dst_subresource.array_layers.len() {
return Err(Box::new(ValidationError {
problem: "the length of `src_subresource.array_layers` does not equal \
the length of `dst_subresource.array_layers`"
.into(),
vuids: &["VUID-VkImageResolve2-layerCount-00267"],
..Default::default()
}));
}
Ok(())
}
pub(crate) fn to_vk2(&self) -> ash::vk::ImageResolve2<'static> {
let &Self {
ref src_subresource,
src_offset,
ref dst_subresource,
dst_offset,
extent,
_ne: _,
} = self;
ash::vk::ImageResolve2::default()
.src_subresource(src_subresource.to_vk())
.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.to_vk())
.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],
})
}
pub(crate) fn to_vk(&self) -> ash::vk::ImageResolve {
let &Self {
ref src_subresource,
src_offset,
ref dst_subresource,
dst_offset,
extent,
_ne: _,
} = self;
ash::vk::ImageResolve {
src_subresource: src_subresource.to_vk(),
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.to_vk(),
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],
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::format::Format;
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.div_ceil(block_extent)
})
.product::<DeviceSize>()
* layer_count as DeviceSize;
num_blocks * format.block_size()
}
#[test]
fn test_required_len_for_format() {
assert_eq!(
required_size_for_format(Format::BC1_RGB_UNORM_BLOCK, [2048, 2048, 1], 1),
2097152
);
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
);
}
}