use crate::{
buffer::{BufferContents, BufferUsage, Subbuffer},
command_buffer::{
allocator::CommandBufferAllocator, auto::Resource, sys::UnsafeCommandBufferBuilder,
AutoCommandBufferBuilder, ResourceInCommand,
},
device::{Device, DeviceOwned, QueueFlags},
format::{ClearColorValue, ClearDepthStencilValue, FormatFeatures},
image::{Image, ImageAspects, ImageLayout, ImageSubresourceRange, ImageUsage},
sync::PipelineStageAccessFlags,
DeviceSize, Requires, RequiresAllOf, RequiresOneOf, SafeDeref, ValidationError, Version,
VulkanObject,
};
use smallvec::{smallvec, SmallVec};
use std::{mem::size_of_val, sync::Arc};
impl<L, A> AutoCommandBufferBuilder<L, A>
where
A: CommandBufferAllocator,
{
pub fn clear_color_image(
&mut self,
clear_info: ClearColorImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_clear_color_image(&clear_info)?;
unsafe { Ok(self.clear_color_image_unchecked(clear_info)) }
}
fn validate_clear_color_image(
&self,
clear_info: &ClearColorImageInfo,
) -> Result<(), Box<ValidationError>> {
self.inner.validate_clear_color_image(clear_info)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdClearColorImage-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn clear_color_image_unchecked(
&mut self,
clear_info: ClearColorImageInfo,
) -> &mut Self {
let &ClearColorImageInfo {
ref image,
image_layout,
clear_value: _,
ref regions,
_ne: _,
} = &clear_info;
self.add_command(
"clear_color_image",
regions
.iter()
.cloned()
.flat_map(|subresource_range| {
[(
ResourceInCommand::Destination.into(),
Resource::Image {
image: image.clone(),
subresource_range,
memory_access: PipelineStageAccessFlags::Clear_TransferWrite,
start_layout: image_layout,
end_layout: image_layout,
},
)]
})
.collect(),
move |out: &mut UnsafeCommandBufferBuilder<A>| {
out.clear_color_image_unchecked(&clear_info);
},
);
self
}
pub fn clear_depth_stencil_image(
&mut self,
clear_info: ClearDepthStencilImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_clear_depth_stencil_image(&clear_info)?;
unsafe { Ok(self.clear_depth_stencil_image_unchecked(clear_info)) }
}
fn validate_clear_depth_stencil_image(
&self,
clear_info: &ClearDepthStencilImageInfo,
) -> Result<(), Box<ValidationError>> {
self.inner.validate_clear_depth_stencil_image(clear_info)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdClearDepthStencilImage-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn clear_depth_stencil_image_unchecked(
&mut self,
clear_info: ClearDepthStencilImageInfo,
) -> &mut Self {
let &ClearDepthStencilImageInfo {
ref image,
image_layout,
clear_value: _,
ref regions,
_ne: _,
} = &clear_info;
self.add_command(
"clear_depth_stencil_image",
regions
.iter()
.cloned()
.flat_map(|subresource_range| {
[(
ResourceInCommand::Destination.into(),
Resource::Image {
image: image.clone(),
subresource_range,
memory_access: PipelineStageAccessFlags::Clear_TransferWrite,
start_layout: image_layout,
end_layout: image_layout,
},
)]
})
.collect(),
move |out: &mut UnsafeCommandBufferBuilder<A>| {
out.clear_depth_stencil_image_unchecked(&clear_info);
},
);
self
}
pub fn fill_buffer(
&mut self,
dst_buffer: Subbuffer<[u32]>,
data: u32,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_fill_buffer(&dst_buffer, data)?;
unsafe { Ok(self.fill_buffer_unchecked(dst_buffer, data)) }
}
fn validate_fill_buffer(
&self,
dst_buffer: &Subbuffer<[u32]>,
data: u32,
) -> Result<(), Box<ValidationError>> {
self.inner.validate_fill_buffer(dst_buffer, data)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdFillBuffer-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn fill_buffer_unchecked(
&mut self,
dst_buffer: Subbuffer<[u32]>,
data: u32,
) -> &mut Self {
self.add_command(
"fill_buffer",
[(
ResourceInCommand::Destination.into(),
Resource::Buffer {
buffer: dst_buffer.as_bytes().clone(),
range: 0..dst_buffer.size(),
memory_access: PipelineStageAccessFlags::Clear_TransferWrite,
},
)]
.into_iter()
.collect(),
move |out: &mut UnsafeCommandBufferBuilder<A>| {
out.fill_buffer_unchecked(&dst_buffer, data);
},
);
self
}
pub fn update_buffer<D, Dd>(
&mut self,
dst_buffer: Subbuffer<D>,
data: Dd,
) -> Result<&mut Self, Box<ValidationError>>
where
D: BufferContents + ?Sized,
Dd: SafeDeref<Target = D> + Send + Sync + 'static,
{
self.validate_update_buffer(
dst_buffer.as_bytes(),
size_of_val(data.deref()) as DeviceSize,
)?;
unsafe { Ok(self.update_buffer_unchecked(dst_buffer, data)) }
}
fn validate_update_buffer(
&self,
dst_buffer: &Subbuffer<[u8]>,
data_size: DeviceSize,
) -> Result<(), Box<ValidationError>> {
self.inner.validate_update_buffer(dst_buffer, data_size)?;
if self.builder_state.render_pass.is_some() {
return Err(Box::new(ValidationError {
problem: "a render pass instance is active".into(),
vuids: &["VUID-vkCmdUpdateBuffer-renderpass"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn update_buffer_unchecked<D, Dd>(
&mut self,
dst_buffer: Subbuffer<D>,
data: Dd,
) -> &mut Self
where
D: BufferContents + ?Sized,
Dd: SafeDeref<Target = D> + Send + Sync + 'static,
{
self.add_command(
"update_buffer",
[(
ResourceInCommand::Destination.into(),
Resource::Buffer {
buffer: dst_buffer.as_bytes().clone(),
range: 0..size_of_val(data.deref()) as DeviceSize,
memory_access: PipelineStageAccessFlags::Clear_TransferWrite,
},
)]
.into_iter()
.collect(),
move |out: &mut UnsafeCommandBufferBuilder<A>| {
out.update_buffer_unchecked(&dst_buffer, &data);
},
);
self
}
}
impl<A> UnsafeCommandBufferBuilder<A>
where
A: CommandBufferAllocator,
{
pub unsafe fn clear_color_image(
&mut self,
clear_info: &ClearColorImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_clear_color_image(clear_info)?;
Ok(self.clear_color_image_unchecked(clear_info))
}
fn validate_clear_color_image(
&self,
clear_info: &ClearColorImageInfo,
) -> Result<(), Box<ValidationError>> {
if !self
.queue_family_properties()
.queue_flags
.intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
{
return Err(Box::new(ValidationError {
problem: "the queue family of the command buffer does not support \
graphics or compute operations"
.into(),
vuids: &["VUID-vkCmdClearColorImage-commandBuffer-cmdpool"],
..Default::default()
}));
}
clear_info
.validate(self.device())
.map_err(|err| err.add_context("clear_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn clear_color_image_unchecked(
&mut self,
clear_info: &ClearColorImageInfo,
) -> &mut Self {
let &ClearColorImageInfo {
ref image,
image_layout,
clear_value,
ref regions,
_ne: _,
} = clear_info;
if regions.is_empty() {
return self;
}
let clear_value_vk = clear_value.into();
let ranges_vk: SmallVec<[_; 8]> = regions
.iter()
.cloned()
.map(ash::vk::ImageSubresourceRange::from)
.collect();
let fns = self.device().fns();
(fns.v1_0.cmd_clear_color_image)(
self.handle(),
image.handle(),
image_layout.into(),
&clear_value_vk,
ranges_vk.len() as u32,
ranges_vk.as_ptr(),
);
self
}
pub unsafe fn clear_depth_stencil_image(
&mut self,
clear_info: &ClearDepthStencilImageInfo,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_clear_depth_stencil_image(clear_info)?;
Ok(self.clear_depth_stencil_image_unchecked(clear_info))
}
fn validate_clear_depth_stencil_image(
&self,
clear_info: &ClearDepthStencilImageInfo,
) -> 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-vkCmdClearDepthStencilImage-commandBuffer-cmdpool"],
..Default::default()
}));
}
clear_info
.validate(self.device())
.map_err(|err| err.add_context("clear_info"))?;
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn clear_depth_stencil_image_unchecked(
&mut self,
clear_info: &ClearDepthStencilImageInfo,
) -> &mut Self {
let &ClearDepthStencilImageInfo {
ref image,
image_layout,
clear_value,
ref regions,
_ne: _,
} = clear_info;
if regions.is_empty() {
return self;
}
let clear_value_vk = clear_value.into();
let ranges_vk: SmallVec<[_; 8]> = regions
.iter()
.cloned()
.map(ash::vk::ImageSubresourceRange::from)
.collect();
let fns = self.device().fns();
(fns.v1_0.cmd_clear_depth_stencil_image)(
self.handle(),
image.handle(),
image_layout.into(),
&clear_value_vk,
ranges_vk.len() as u32,
ranges_vk.as_ptr(),
);
self
}
pub unsafe fn fill_buffer(
&mut self,
dst_buffer: &Subbuffer<[u32]>,
data: u32,
) -> Result<&mut Self, Box<ValidationError>> {
self.validate_fill_buffer(dst_buffer, data)?;
Ok(self.fill_buffer_unchecked(dst_buffer, data))
}
fn validate_fill_buffer(
&self,
dst_buffer: &Subbuffer<[u32]>,
_data: u32,
) -> Result<(), Box<ValidationError>> {
let device = self.device();
let queue_family_properties = self.queue_family_properties();
if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
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-vkCmdFillBuffer-commandBuffer-cmdpool"],
..Default::default()
}));
}
} else {
if !queue_family_properties
.queue_flags
.intersects(QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
{
return Err(Box::new(ValidationError {
problem: "the queue family of the command buffer does not support \
graphics or compute operations"
.into(),
vuids: &["VUID-vkCmdFillBuffer-commandBuffer-00030"],
..Default::default()
}));
}
}
assert_eq!(device, dst_buffer.device());
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-vkCmdFillBuffer-dstBuffer-00029"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn fill_buffer_unchecked(
&mut self,
dst_buffer: &Subbuffer<[u32]>,
data: u32,
) -> &mut Self {
let fns = self.device().fns();
(fns.v1_0.cmd_fill_buffer)(
self.handle(),
dst_buffer.buffer().handle(),
dst_buffer.offset(),
dst_buffer.size(),
data,
);
self
}
pub unsafe fn update_buffer<D>(
&mut self,
dst_buffer: &Subbuffer<D>,
data: &D,
) -> Result<&mut Self, Box<ValidationError>>
where
D: BufferContents + ?Sized,
{
if size_of_val(data) == 0 {
return Ok(self);
}
self.validate_update_buffer(dst_buffer.as_bytes(), size_of_val(data) as DeviceSize)?;
Ok(self.update_buffer_unchecked(dst_buffer, data))
}
fn validate_update_buffer(
&self,
dst_buffer: &Subbuffer<[u8]>,
data_size: DeviceSize,
) -> 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-vkCmdUpdateBuffer-commandBuffer-cmdpool"],
..Default::default()
}));
}
let device = self.device();
assert_eq!(device, dst_buffer.device());
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-vkCmdUpdateBuffer-dstBuffer-00034"],
..Default::default()
}));
}
if data_size > dst_buffer.size() {
return Err(Box::new(ValidationError {
problem: "the size of `data` is greater than `dst_buffer.size()`".into(),
vuids: &[
"VUID-vkCmdUpdateBuffer-dstOffset-00032",
"VUID-vkCmdUpdateBuffer-dataSize-00033",
],
..Default::default()
}));
}
if dst_buffer.offset() % 4 != 0 {
return Err(Box::new(ValidationError {
context: "dst_buffer.offset()".into(),
problem: "is not a multiple of 4".into(),
vuids: &["VUID-vkCmdUpdateBuffer-dstOffset-00036"],
..Default::default()
}));
}
if data_size > 65536 {
return Err(Box::new(ValidationError {
context: "data".into(),
problem: "the size is greater than 65536 bytes".into(),
vuids: &["VUID-vkCmdUpdateBuffer-dataSize-00037"],
..Default::default()
}));
}
if data_size % 4 != 0 {
return Err(Box::new(ValidationError {
context: "data".into(),
problem: "the size is not a multiple of 4".into(),
vuids: &["VUID-vkCmdUpdateBuffer-dataSize-00038"],
..Default::default()
}));
}
Ok(())
}
#[cfg_attr(not(feature = "document_unchecked"), doc(hidden))]
pub unsafe fn update_buffer_unchecked<D>(
&mut self,
dst_buffer: &Subbuffer<D>,
data: &D,
) -> &mut Self
where
D: BufferContents + ?Sized,
{
if size_of_val(data) == 0 {
return self;
}
let fns = self.device().fns();
(fns.v1_0.cmd_update_buffer)(
self.handle(),
dst_buffer.buffer().handle(),
dst_buffer.offset(),
size_of_val(data) as DeviceSize,
data as *const _ as *const _,
);
self
}
}
#[derive(Clone, Debug)]
pub struct ClearColorImageInfo {
pub image: Arc<Image>,
pub image_layout: ImageLayout,
pub clear_value: ClearColorValue,
pub regions: SmallVec<[ImageSubresourceRange; 1]>,
pub _ne: crate::NonExhaustive,
}
impl ClearColorImageInfo {
#[inline]
pub fn image(image: Arc<Image>) -> Self {
let range = image.subresource_range();
Self {
image,
image_layout: ImageLayout::TransferDstOptimal,
clear_value: ClearColorValue::Float([0.0; 4]),
regions: smallvec![range],
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref image,
image_layout,
clear_value: _,
ref regions,
_ne: _,
} = self;
image_layout.validate_device(device).map_err(|err| {
err.add_context("image_layout")
.set_vuids(&["VUID-vkCmdClearColorImage-imageLayout-parameter"])
})?;
assert_eq!(device, image.device().as_ref());
if !image.usage().intersects(ImageUsage::TRANSFER_DST) {
return Err(Box::new(ValidationError {
context: "image.usage()".into(),
problem: "does not contain `ImageUsage::TRANSFER_DST`".into(),
vuids: &["VUID-vkCmdClearColorImage-image-00002"],
..Default::default()
}));
}
if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
if !image
.format_features()
.intersects(FormatFeatures::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
context: "image.format_features()".into(),
problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(),
vuids: &["VUID-vkCmdClearColorImage-image-01993"],
..Default::default()
}));
}
}
let image_aspects = image.format().aspects();
if image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
return Err(Box::new(ValidationError {
context: "image.format()".into(),
problem: "is a depth/stencil format".into(),
vuids: &["VUID-vkCmdClearColorImage-image-00007"],
..Default::default()
}));
}
if image.format().compression().is_some() {
return Err(Box::new(ValidationError {
context: "image.format()".into(),
problem: "is a compressed format".into(),
vuids: &["VUID-vkCmdClearColorImage-image-00007"],
..Default::default()
}));
}
if image.format().ycbcr_chroma_sampling().is_some() {
return Err(Box::new(ValidationError {
context: "image.format()".into(),
problem: "is a YCbCr format".into(),
vuids: &["VUID-vkCmdClearColorImage-image-01545"],
..Default::default()
}));
}
if !matches!(
image_layout,
ImageLayout::TransferDstOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "image_layout".into(),
problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-vkCmdClearColorImage-imageLayout-01394"],
..Default::default()
}));
}
for (region_index, subresource_range) in regions.iter().enumerate() {
subresource_range
.validate(device)
.map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
if !image_aspects.contains(subresource_range.aspects) {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].aspects` is not a subset of `image.format().aspects()`",
region_index
)
.into(),
vuids: &["VUID-vkCmdClearColorImage-aspectMask-02498"],
..Default::default()
}));
}
if subresource_range.mip_levels.end > image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].mip_levels.end` is greater than `image.mip_levels()`",
region_index
)
.into(),
vuids: &[
"VUID-vkCmdClearColorImage-baseMipLevel-01470",
"VUID-vkCmdClearColorImage-pRanges-01692",
],
..Default::default()
}));
}
if subresource_range.array_layers.end > image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].array_layers.end` is greater than `image.array_layers()`",
region_index
)
.into(),
vuids: &[
"VUID-vkCmdClearColorImage-baseArrayLayer-01472",
"VUID-vkCmdClearColorImage-pRanges-01693",
],
..Default::default()
}));
}
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct ClearDepthStencilImageInfo {
pub image: Arc<Image>,
pub image_layout: ImageLayout,
pub clear_value: ClearDepthStencilValue,
pub regions: SmallVec<[ImageSubresourceRange; 1]>,
pub _ne: crate::NonExhaustive,
}
impl ClearDepthStencilImageInfo {
#[inline]
pub fn image(image: Arc<Image>) -> Self {
let range = image.subresource_range();
Self {
image,
image_layout: ImageLayout::TransferDstOptimal,
clear_value: ClearDepthStencilValue::default(),
regions: smallvec![range],
_ne: crate::NonExhaustive(()),
}
}
pub(crate) fn validate(&self, device: &Device) -> Result<(), Box<ValidationError>> {
let &Self {
ref image,
image_layout,
clear_value,
ref regions,
_ne: _,
} = self;
image_layout.validate_device(device).map_err(|err| {
err.add_context("image_layout")
.set_vuids(&["VUID-vkCmdClearDepthStencilImage-imageLayout-parameter"])
})?;
assert_eq!(device, image.device().as_ref());
if device.api_version() >= Version::V1_1 || device.enabled_extensions().khr_maintenance1 {
if !image
.format_features()
.intersects(FormatFeatures::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
context: "image.format_features()".into(),
problem: "does not contain `FormatFeatures::TRANSFER_DST`".into(),
vuids: &["VUID-vkCmdClearDepthStencilImage-image-01994"],
..Default::default()
}));
}
}
let image_aspects = image.format().aspects();
if !image_aspects.intersects(ImageAspects::DEPTH | ImageAspects::STENCIL) {
return Err(Box::new(ValidationError {
context: "image.format()".into(),
problem: "is not a depth/stencil format".into(),
vuids: &["VUID-vkCmdClearDepthStencilImage-image-00014"],
..Default::default()
}));
}
if !matches!(
image_layout,
ImageLayout::TransferDstOptimal | ImageLayout::General
) {
return Err(Box::new(ValidationError {
context: "image_layout".into(),
problem: "is not `ImageLayout::TransferDstOptimal` or `ImageLayout::General`"
.into(),
vuids: &["VUID-vkCmdClearDepthStencilImage-imageLayout-00012"],
..Default::default()
}));
}
if !(0.0..=1.0).contains(&clear_value.depth)
&& !device.enabled_extensions().ext_depth_range_unrestricted
{
return Err(Box::new(ValidationError {
context: "clear_value.depth".into(),
problem: "is not between `0.0` and `1.0` inclusive".into(),
requires_one_of: RequiresOneOf(&[RequiresAllOf(&[Requires::DeviceExtension(
"ext_depth_range_unrestricted",
)])]),
vuids: &["VUID-VkClearDepthStencilValue-depth-00022"],
}));
}
for (region_index, subresource_range) in regions.iter().enumerate() {
subresource_range
.validate(device)
.map_err(|err| err.add_context(format!("regions[{}]", region_index)))?;
if !image_aspects.contains(subresource_range.aspects) {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].aspects` is not a subset of `image.format().aspects()`",
region_index
)
.into(),
vuids: &[
"VUID-vkCmdClearDepthStencilImage-aspectMask-02824",
"VUID-vkCmdClearDepthStencilImage-image-02825",
"VUID-vkCmdClearDepthStencilImage-image-02826",
],
..Default::default()
}));
}
if subresource_range.aspects.intersects(ImageAspects::STENCIL)
&& !image
.stencil_usage()
.unwrap_or(image.usage())
.intersects(ImageUsage::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].aspects` contains `ImageAspects::STENCIL`, but \
`image.stencil_usage()` does not contain `ImageUsage::TRANSFER_DST`",
region_index
)
.into(),
vuids: &[
"VUID-vkCmdClearDepthStencilImage-pRanges-02658",
"VUID-vkCmdClearDepthStencilImage-pRanges-02659",
],
..Default::default()
}));
}
if !(subresource_range.aspects - ImageAspects::STENCIL).is_empty()
&& !image.usage().intersects(ImageUsage::TRANSFER_DST)
{
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].aspects` contains aspects other than \
`ImageAspects::STENCIL`, but \
`image.usage()` does not contain `ImageUsage::TRANSFER_DST`",
region_index
)
.into(),
vuids: &["VUID-vkCmdClearDepthStencilImage-pRanges-02660"],
..Default::default()
}));
}
if subresource_range.mip_levels.end > image.mip_levels() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].mip_levels.end` is greater than `image.mip_levels()`",
region_index
)
.into(),
vuids: &[
"VUID-vkCmdClearDepthStencilImage-baseMipLevel-01474",
"VUID-vkCmdClearDepthStencilImage-pRanges-01694",
],
..Default::default()
}));
}
if subresource_range.array_layers.end > image.array_layers() {
return Err(Box::new(ValidationError {
problem: format!(
"`regions[{}].array_layers.end` is greater than `image.array_layers()`",
region_index
)
.into(),
vuids: &[
"VUID-vkCmdClearDepthStencilImage-baseArrayLayer-01476",
"VUID-vkCmdClearDepthStencilImage-pRanges-01695",
],
..Default::default()
}));
}
}
Ok(())
}
}