// Copyright (c) 2017 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. use super::layout::{DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorType}; use crate::{ buffer::{view::BufferView, BufferUsage, Subbuffer}, device::DeviceOwned, image::{view::ImageViewType, ImageAspects, ImageType, ImageUsage, ImageViewAbstract}, sampler::{Sampler, SamplerImageViewIncompatibleError}, DeviceSize, RequiresOneOf, VulkanObject, }; use smallvec::SmallVec; use std::{ error::Error, fmt::{Display, Error as FmtError, Formatter}, ops::Range, ptr, sync::Arc, }; /// Represents a single write operation to the binding of a descriptor set. /// /// `WriteDescriptorSet` specifies the binding number and target array index, and includes one or /// more resources of a given type that need to be written to that location. Two constructors are /// provided for each resource type: /// - The basic constructor variant writes a single element to array index 0. It is intended for /// non-arrayed bindings, where `descriptor_count` in the descriptor set layout is 1. /// - The `_array` variant writes several elements and allows specifying the target array index. /// At least one element must be provided; a panic results if the provided iterator is empty. pub struct WriteDescriptorSet { binding: u32, first_array_element: u32, elements: WriteDescriptorSetElements, } impl WriteDescriptorSet { /// Write an empty element to array element 0. /// /// This is used for push descriptors in combination with `Sampler` descriptors that have /// immutable samplers in the layout. The Vulkan spec requires these elements to be explicitly /// written, but since there is no data to write, a dummy write is provided instead. /// /// For regular descriptor sets, the data for such descriptors is automatically valid, and dummy /// writes are not allowed. #[inline] pub fn none(binding: u32) -> Self { Self::none_array(binding, 0, 1) } /// Write a number of consecutive empty elements. /// /// See [`none`](Self::none) for more information. #[inline] pub fn none_array(binding: u32, first_array_element: u32, num_elements: u32) -> Self { assert!(num_elements != 0); Self { binding, first_array_element, elements: WriteDescriptorSetElements::None(num_elements), } } /// Write a single buffer to array element 0, with the bound range covering the whole buffer. /// /// For dynamic buffer bindings, this will bind the whole buffer, and only a dynamic offset /// of zero will be valid, which is probably not what you want. /// Use [`buffer_with_range`](Self::buffer_with_range) instead. #[inline] pub fn buffer(binding: u32, buffer: Subbuffer) -> Self { let range = 0..buffer.size(); Self::buffer_with_range_array(binding, 0, [(buffer, range)]) } /// Write a number of consecutive buffer elements. /// /// See [`buffer`](Self::buffer) for more information. #[inline] pub fn buffer_array( binding: u32, first_array_element: u32, elements: impl IntoIterator>, ) -> Self { Self::buffer_with_range_array( binding, first_array_element, elements.into_iter().map(|buffer| { let range = 0..buffer.size(); (buffer, range) }), ) } /// Write a single buffer to array element 0, specifying the range of the buffer to be bound. /// /// `range` is the slice of bytes in `buffer` that will be made available to the shader. /// `range` must not be outside the range `buffer`. /// /// For dynamic buffer bindings, `range` specifies the slice that is to be bound if the /// dynamic offset were zero. When binding the descriptor set, the effective value of `range` /// shifts forward by the offset that was provided. For example, if `range` is specified as /// `0..8` when writing the descriptor set, and then when binding the descriptor set the /// offset `16` is used, then the range of `buffer` that will actually be bound is `16..24`. #[inline] pub fn buffer_with_range( binding: u32, buffer: Subbuffer, range: Range, ) -> Self { Self::buffer_with_range_array(binding, 0, [(buffer, range)]) } /// Write a number of consecutive buffer elements, specifying the ranges of the buffers to be /// bound. /// /// See [`buffer_with_range`](Self::buffer_with_range) for more information. pub fn buffer_with_range_array( binding: u32, first_array_element: u32, elements: impl IntoIterator, Range)>, ) -> Self { let elements: SmallVec<_> = elements .into_iter() .map(|(buffer, range)| (buffer.into_bytes(), range)) .collect(); assert!(!elements.is_empty()); Self { binding, first_array_element, elements: WriteDescriptorSetElements::Buffer(elements), } } /// Write a single buffer view to array element 0. #[inline] pub fn buffer_view(binding: u32, buffer_view: Arc) -> Self { Self::buffer_view_array(binding, 0, [buffer_view]) } /// Write a number of consecutive buffer view elements. pub fn buffer_view_array( binding: u32, first_array_element: u32, elements: impl IntoIterator>, ) -> Self { let elements: SmallVec<_> = elements.into_iter().collect(); assert!(!elements.is_empty()); Self { binding, first_array_element, elements: WriteDescriptorSetElements::BufferView(elements), } } /// Write a single image view to array element 0. #[inline] pub fn image_view(binding: u32, image_view: Arc) -> Self { Self::image_view_array(binding, 0, [image_view]) } /// Write a number of consecutive image view elements. pub fn image_view_array( binding: u32, first_array_element: u32, elements: impl IntoIterator>, ) -> Self { let elements: SmallVec<_> = elements.into_iter().collect(); assert!(!elements.is_empty()); Self { binding, first_array_element, elements: WriteDescriptorSetElements::ImageView(elements), } } /// Write a single image view and sampler to array element 0. #[inline] pub fn image_view_sampler( binding: u32, image_view: Arc, sampler: Arc, ) -> Self { Self::image_view_sampler_array(binding, 0, [(image_view, sampler)]) } /// Write a number of consecutive image view and sampler elements. pub fn image_view_sampler_array( binding: u32, first_array_element: u32, elements: impl IntoIterator, Arc)>, ) -> Self { let elements: SmallVec<_> = elements.into_iter().collect(); assert!(!elements.is_empty()); Self { binding, first_array_element, elements: WriteDescriptorSetElements::ImageViewSampler(elements), } } /// Write a single sampler to array element 0. #[inline] pub fn sampler(binding: u32, sampler: Arc) -> Self { Self::sampler_array(binding, 0, [sampler]) } /// Write a number of consecutive sampler elements. pub fn sampler_array( binding: u32, first_array_element: u32, elements: impl IntoIterator>, ) -> Self { let elements: SmallVec<_> = elements.into_iter().collect(); assert!(!elements.is_empty()); Self { binding, first_array_element, elements: WriteDescriptorSetElements::Sampler(elements), } } /// Returns the binding number that is updated by this descriptor write. #[inline] pub fn binding(&self) -> u32 { self.binding } /// Returns the first array element in the binding that is updated by this descriptor write. #[inline] pub fn first_array_element(&self) -> u32 { self.first_array_element } /// Returns a reference to the elements held by this descriptor write. #[inline] pub fn elements(&self) -> &WriteDescriptorSetElements { &self.elements } pub(crate) fn to_vulkan_info(&self, descriptor_type: DescriptorType) -> DescriptorWriteInfo { match &self.elements { WriteDescriptorSetElements::None(num_elements) => { debug_assert!(matches!(descriptor_type, DescriptorType::Sampler)); DescriptorWriteInfo::Image( std::iter::repeat_with(|| ash::vk::DescriptorImageInfo { sampler: ash::vk::Sampler::null(), image_view: ash::vk::ImageView::null(), image_layout: ash::vk::ImageLayout::UNDEFINED, }) .take(*num_elements as usize) .collect(), ) } WriteDescriptorSetElements::Buffer(elements) => { debug_assert!(matches!( descriptor_type, DescriptorType::UniformBuffer | DescriptorType::StorageBuffer | DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic )); DescriptorWriteInfo::Buffer( elements .iter() .map(|(buffer, range)| { debug_assert!(!range.is_empty()); debug_assert!(range.end <= buffer.buffer().size()); ash::vk::DescriptorBufferInfo { buffer: buffer.buffer().handle(), offset: buffer.offset() + range.start, range: range.end - range.start, } }) .collect(), ) } WriteDescriptorSetElements::BufferView(elements) => { debug_assert!(matches!( descriptor_type, DescriptorType::UniformTexelBuffer | DescriptorType::StorageTexelBuffer )); DescriptorWriteInfo::BufferView( elements .iter() .map(|buffer_view| buffer_view.handle()) .collect(), ) } WriteDescriptorSetElements::ImageView(elements) => { // Note: combined image sampler can occur with immutable samplers debug_assert!(matches!( descriptor_type, DescriptorType::CombinedImageSampler | DescriptorType::SampledImage | DescriptorType::StorageImage | DescriptorType::InputAttachment )); DescriptorWriteInfo::Image( elements .iter() .map(|image_view| { let layouts = image_view.image().descriptor_layouts().expect( "descriptor_layouts must return Some when used in an image view", ); ash::vk::DescriptorImageInfo { sampler: ash::vk::Sampler::null(), image_view: image_view.handle(), image_layout: layouts.layout_for(descriptor_type).into(), } }) .collect(), ) } WriteDescriptorSetElements::ImageViewSampler(elements) => { debug_assert!(matches!( descriptor_type, DescriptorType::CombinedImageSampler )); DescriptorWriteInfo::Image( elements .iter() .map(|(image_view, sampler)| { let layouts = image_view.image().descriptor_layouts().expect( "descriptor_layouts must return Some when used in an image view", ); ash::vk::DescriptorImageInfo { sampler: sampler.handle(), image_view: image_view.handle(), image_layout: layouts.layout_for(descriptor_type).into(), } }) .collect(), ) } WriteDescriptorSetElements::Sampler(elements) => { debug_assert!(matches!(descriptor_type, DescriptorType::Sampler)); DescriptorWriteInfo::Image( elements .iter() .map(|sampler| ash::vk::DescriptorImageInfo { sampler: sampler.handle(), image_view: ash::vk::ImageView::null(), image_layout: ash::vk::ImageLayout::UNDEFINED, }) .collect(), ) } } } pub(crate) fn to_vulkan( &self, dst_set: ash::vk::DescriptorSet, descriptor_type: DescriptorType, ) -> ash::vk::WriteDescriptorSet { ash::vk::WriteDescriptorSet { dst_set, dst_binding: self.binding, dst_array_element: self.first_array_element, descriptor_count: 0, descriptor_type: descriptor_type.into(), p_image_info: ptr::null(), p_buffer_info: ptr::null(), p_texel_buffer_view: ptr::null(), ..Default::default() } } } /// The elements held by a `WriteDescriptorSet`. pub enum WriteDescriptorSetElements { None(u32), Buffer(SmallVec<[(Subbuffer<[u8]>, Range); 1]>), BufferView(SmallVec<[Arc; 1]>), ImageView(SmallVec<[Arc; 1]>), ImageViewSampler(SmallVec<[(Arc, Arc); 1]>), Sampler(SmallVec<[Arc; 1]>), } impl WriteDescriptorSetElements { /// Returns the number of elements. #[inline] pub fn len(&self) -> u32 { match self { Self::None(num_elements) => *num_elements, Self::Buffer(elements) => elements.len() as u32, Self::BufferView(elements) => elements.len() as u32, Self::ImageView(elements) => elements.len() as u32, Self::ImageViewSampler(elements) => elements.len() as u32, Self::Sampler(elements) => elements.len() as u32, } } } #[derive(Clone, Debug)] pub(crate) enum DescriptorWriteInfo { Image(SmallVec<[ash::vk::DescriptorImageInfo; 1]>), Buffer(SmallVec<[ash::vk::DescriptorBufferInfo; 1]>), BufferView(SmallVec<[ash::vk::BufferView; 1]>), } pub(crate) fn check_descriptor_write<'a>( write: &WriteDescriptorSet, layout: &'a DescriptorSetLayout, variable_descriptor_count: u32, ) -> Result<&'a DescriptorSetLayoutBinding, DescriptorSetUpdateError> { fn provided_element_type(elements: &WriteDescriptorSetElements) -> &'static str { match elements { WriteDescriptorSetElements::None(_) => "none", WriteDescriptorSetElements::Buffer(_) => "buffer", WriteDescriptorSetElements::BufferView(_) => "buffer_view", WriteDescriptorSetElements::ImageView(_) => "image_view", WriteDescriptorSetElements::ImageViewSampler(_) => "image_view_sampler", WriteDescriptorSetElements::Sampler(_) => "sampler", } } let device = layout.device(); let layout_binding = match layout.bindings().get(&write.binding()) { Some(binding) => binding, None => { return Err(DescriptorSetUpdateError::InvalidBinding { binding: write.binding(), }) } }; let max_descriptor_count = if layout_binding.variable_descriptor_count { variable_descriptor_count } else { layout_binding.descriptor_count }; let binding = write.binding(); let elements = write.elements(); let num_elements = elements.len(); debug_assert!(num_elements != 0); let descriptor_range_start = write.first_array_element(); let descriptor_range_end = descriptor_range_start + num_elements; if descriptor_range_end > max_descriptor_count { return Err(DescriptorSetUpdateError::ArrayIndexOutOfBounds { binding, available_count: max_descriptor_count, written_count: descriptor_range_end, }); } match layout_binding.descriptor_type { DescriptorType::Sampler => { if layout_binding.immutable_samplers.is_empty() { let elements = if let WriteDescriptorSetElements::Sampler(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["sampler"], }); }; for (index, sampler) in elements.iter().enumerate() { assert_eq!(device, sampler.device()); if sampler.sampler_ycbcr_conversion().is_some() { return Err(DescriptorSetUpdateError::SamplerHasSamplerYcbcrConversion { binding: write.binding(), index: descriptor_range_start + index as u32, }); } } } else if layout.push_descriptor() { // For push descriptors, we must write a dummy element. if let WriteDescriptorSetElements::None(_) = elements { // Do nothing } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["none"], }); } } else { // For regular descriptors, no element must be written. return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &[], }); } } DescriptorType::CombinedImageSampler => { if layout_binding.immutable_samplers.is_empty() { let elements = if let WriteDescriptorSetElements::ImageViewSampler(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["image_view_sampler"], }); }; for (index, (image_view, sampler)) in elements.iter().enumerate() { assert_eq!(device, image_view.device()); assert_eq!(device, sampler.device()); // VUID-VkWriteDescriptorSet-descriptorType-00337 if !image_view.usage().intersects(ImageUsage::SAMPLED) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "sampled", }); } // VUID-VkDescriptorImageInfo-imageView-00343 if matches!( image_view.view_type(), ImageViewType::Dim2d | ImageViewType::Dim2dArray ) && image_view.image().inner().image.dimensions().image_type() == ImageType::Dim3d { return Err(DescriptorSetUpdateError::ImageView2dFrom3d { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkDescriptorImageInfo-imageView-01976 if image_view .subresource_range() .aspects .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) { return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkDescriptorImageInfo-mutableComparisonSamplers-04450 if device.enabled_extensions().khr_portability_subset && !device.enabled_features().mutable_comparison_samplers && sampler.compare().is_some() { return Err(DescriptorSetUpdateError::RequirementNotMet { binding: write.binding(), index: descriptor_range_start + index as u32, required_for: "this device is a portability subset device, and \ `sampler.compare()` is `Some`", requires_one_of: RequiresOneOf { features: &["mutable_comparison_samplers"], ..Default::default() }, }); } if image_view.sampler_ycbcr_conversion().is_some() { return Err( DescriptorSetUpdateError::ImageViewHasSamplerYcbcrConversion { binding: write.binding(), index: descriptor_range_start + index as u32, }, ); } if sampler.sampler_ycbcr_conversion().is_some() { return Err(DescriptorSetUpdateError::SamplerHasSamplerYcbcrConversion { binding: write.binding(), index: descriptor_range_start + index as u32, }); } if let Err(error) = sampler.check_can_sample(image_view.as_ref()) { return Err(DescriptorSetUpdateError::ImageViewIncompatibleSampler { binding: write.binding(), index: descriptor_range_start + index as u32, error, }); } } } else { let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["image_view"], }); }; let immutable_samplers = &layout_binding.immutable_samplers [descriptor_range_start as usize..descriptor_range_end as usize]; for (index, (image_view, sampler)) in elements.iter().zip(immutable_samplers).enumerate() { assert_eq!(device, image_view.device()); // VUID-VkWriteDescriptorSet-descriptorType-00337 if !image_view.usage().intersects(ImageUsage::SAMPLED) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "sampled", }); } // VUID-VkDescriptorImageInfo-imageView-00343 if matches!( image_view.view_type(), ImageViewType::Dim2d | ImageViewType::Dim2dArray ) && image_view.image().inner().image.dimensions().image_type() == ImageType::Dim3d { return Err(DescriptorSetUpdateError::ImageView2dFrom3d { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkDescriptorImageInfo-imageView-01976 if image_view .subresource_range() .aspects .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) { return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { binding: write.binding(), index: descriptor_range_start + index as u32, }); } if let Err(error) = sampler.check_can_sample(image_view.as_ref()) { return Err(DescriptorSetUpdateError::ImageViewIncompatibleSampler { binding: write.binding(), index: descriptor_range_start + index as u32, error, }); } } } } DescriptorType::SampledImage => { let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["image_view"], }); }; for (index, image_view) in elements.iter().enumerate() { assert_eq!(device, image_view.device()); // VUID-VkWriteDescriptorSet-descriptorType-00337 if !image_view.usage().intersects(ImageUsage::SAMPLED) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "sampled", }); } // VUID-VkDescriptorImageInfo-imageView-00343 if matches!( image_view.view_type(), ImageViewType::Dim2d | ImageViewType::Dim2dArray ) && image_view.image().inner().image.dimensions().image_type() == ImageType::Dim3d { return Err(DescriptorSetUpdateError::ImageView2dFrom3d { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkDescriptorImageInfo-imageView-01976 if image_view .subresource_range() .aspects .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) { return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkWriteDescriptorSet-descriptorType-01946 if image_view.sampler_ycbcr_conversion().is_some() { return Err( DescriptorSetUpdateError::ImageViewHasSamplerYcbcrConversion { binding: write.binding(), index: descriptor_range_start + index as u32, }, ); } } } DescriptorType::StorageImage => { let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["image_view"], }); }; for (index, image_view) in elements.iter().enumerate() { assert_eq!(device, image_view.device()); // VUID-VkWriteDescriptorSet-descriptorType-00339 if !image_view.usage().intersects(ImageUsage::STORAGE) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "storage", }); } // VUID-VkDescriptorImageInfo-imageView-00343 if matches!( image_view.view_type(), ImageViewType::Dim2d | ImageViewType::Dim2dArray ) && image_view.image().inner().image.dimensions().image_type() == ImageType::Dim3d { return Err(DescriptorSetUpdateError::ImageView2dFrom3d { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkDescriptorImageInfo-imageView-01976 if image_view .subresource_range() .aspects .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) { return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkWriteDescriptorSet-descriptorType-00336 if !image_view.component_mapping().is_identity() { return Err(DescriptorSetUpdateError::ImageViewNotIdentitySwizzled { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID?? if image_view.sampler_ycbcr_conversion().is_some() { return Err( DescriptorSetUpdateError::ImageViewHasSamplerYcbcrConversion { binding: write.binding(), index: descriptor_range_start + index as u32, }, ); } } } DescriptorType::UniformTexelBuffer => { let elements = if let WriteDescriptorSetElements::BufferView(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["buffer_view"], }); }; for (index, buffer_view) in elements.iter().enumerate() { assert_eq!(device, buffer_view.device()); if !buffer_view .buffer() .buffer() .usage() .intersects(BufferUsage::UNIFORM_TEXEL_BUFFER) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "uniform_texel_buffer", }); } } } DescriptorType::StorageTexelBuffer => { let elements = if let WriteDescriptorSetElements::BufferView(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["buffer_view"], }); }; for (index, buffer_view) in elements.iter().enumerate() { assert_eq!(device, buffer_view.device()); // TODO: storage_texel_buffer_atomic if !buffer_view .buffer() .buffer() .usage() .intersects(BufferUsage::STORAGE_TEXEL_BUFFER) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "storage_texel_buffer", }); } } } DescriptorType::UniformBuffer | DescriptorType::UniformBufferDynamic => { let elements = if let WriteDescriptorSetElements::Buffer(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["buffer"], }); }; for (index, (buffer, range)) in elements.iter().enumerate() { assert_eq!(device, buffer.device()); if !buffer .buffer() .usage() .intersects(BufferUsage::UNIFORM_BUFFER) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "uniform_buffer", }); } assert!(!range.is_empty()); if range.end > buffer.size() { return Err(DescriptorSetUpdateError::RangeOutOfBufferBounds { binding: write.binding(), index: descriptor_range_start + index as u32, range_end: range.end, buffer_size: buffer.size(), }); } } } DescriptorType::StorageBuffer | DescriptorType::StorageBufferDynamic => { let elements = if let WriteDescriptorSetElements::Buffer(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["buffer"], }); }; for (index, (buffer, range)) in elements.iter().enumerate() { assert_eq!(device, buffer.device()); if !buffer .buffer() .usage() .intersects(BufferUsage::STORAGE_BUFFER) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "storage_buffer", }); } assert!(!range.is_empty()); if range.end > buffer.size() { return Err(DescriptorSetUpdateError::RangeOutOfBufferBounds { binding: write.binding(), index: descriptor_range_start + index as u32, range_end: range.end, buffer_size: buffer.size(), }); } } } DescriptorType::InputAttachment => { let elements = if let WriteDescriptorSetElements::ImageView(elements) = elements { elements } else { return Err(DescriptorSetUpdateError::IncompatibleElementType { binding, provided_element_type: provided_element_type(elements), allowed_element_types: &["image_view"], }); }; for (index, image_view) in elements.iter().enumerate() { assert_eq!(device, image_view.device()); // VUID-VkWriteDescriptorSet-descriptorType-00338 if !image_view.usage().intersects(ImageUsage::INPUT_ATTACHMENT) { return Err(DescriptorSetUpdateError::MissingUsage { binding: write.binding(), index: descriptor_range_start + index as u32, usage: "input_attachment", }); } // VUID-VkDescriptorImageInfo-imageView-00343 if matches!( image_view.view_type(), ImageViewType::Dim2d | ImageViewType::Dim2dArray ) && image_view.image().inner().image.dimensions().image_type() == ImageType::Dim3d { return Err(DescriptorSetUpdateError::ImageView2dFrom3d { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkDescriptorImageInfo-imageView-01976 if image_view .subresource_range() .aspects .contains(ImageAspects::DEPTH | ImageAspects::STENCIL) { return Err(DescriptorSetUpdateError::ImageViewDepthAndStencil { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID-VkWriteDescriptorSet-descriptorType-00336 if !image_view.component_mapping().is_identity() { return Err(DescriptorSetUpdateError::ImageViewNotIdentitySwizzled { binding: write.binding(), index: descriptor_range_start + index as u32, }); } // VUID?? if image_view.sampler_ycbcr_conversion().is_some() { return Err( DescriptorSetUpdateError::ImageViewHasSamplerYcbcrConversion { binding: write.binding(), index: descriptor_range_start + index as u32, }, ); } // VUID?? if image_view.view_type().is_arrayed() { return Err(DescriptorSetUpdateError::ImageViewIsArrayed { binding: write.binding(), index: descriptor_range_start + index as u32, }); } } } } Ok(layout_binding) } #[derive(Clone, Copy, Debug)] pub enum DescriptorSetUpdateError { RequirementNotMet { binding: u32, index: u32, required_for: &'static str, requires_one_of: RequiresOneOf, }, /// Tried to write more elements than were available in a binding. ArrayIndexOutOfBounds { /// Binding that is affected. binding: u32, /// Number of available descriptors in the binding. available_count: u32, /// The number of descriptors that were in the update. written_count: u32, }, /// Tried to write an image view with a 2D type and a 3D underlying image. ImageView2dFrom3d { binding: u32, index: u32 }, /// Tried to write an image view that has both the `depth` and `stencil` aspects. ImageViewDepthAndStencil { binding: u32, index: u32 }, /// Tried to write an image view with an attached sampler YCbCr conversion to a binding that /// does not support it. ImageViewHasSamplerYcbcrConversion { binding: u32, index: u32 }, /// Tried to write an image view of an arrayed type to a descriptor type that does not support /// it. ImageViewIsArrayed { binding: u32, index: u32 }, /// Tried to write an image view that was not compatible with the sampler that was provided as /// part of the update or immutably in the layout. ImageViewIncompatibleSampler { binding: u32, index: u32, error: SamplerImageViewIncompatibleError, }, /// Tried to write an image view to a descriptor type that requires it to be identity swizzled, /// but it was not. ImageViewNotIdentitySwizzled { binding: u32, index: u32 }, /// Tried to write an element type that was not compatible with the descriptor type in the /// layout. IncompatibleElementType { binding: u32, provided_element_type: &'static str, allowed_element_types: &'static [&'static str], }, /// Tried to write to a nonexistent binding. InvalidBinding { binding: u32 }, /// A resource was missing a usage flag that was required. MissingUsage { binding: u32, index: u32, usage: &'static str, }, /// The end of the provided `range` for a buffer is larger than the size of the buffer. RangeOutOfBufferBounds { binding: u32, index: u32, range_end: DeviceSize, buffer_size: DeviceSize, }, /// Tried to write a sampler that has an attached sampler YCbCr conversion. SamplerHasSamplerYcbcrConversion { binding: u32, index: u32 }, } impl Error for DescriptorSetUpdateError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { Self::ImageViewIncompatibleSampler { error, .. } => Some(error), _ => None, } } } impl Display for DescriptorSetUpdateError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::RequirementNotMet { binding, index, required_for, requires_one_of, } => write!( f, "a requirement on binding {} index {} was not met for: {}; requires one of: {}", binding, index, required_for, requires_one_of, ), Self::ArrayIndexOutOfBounds { binding, available_count, written_count, } => write!( f, "tried to write up to element {} to binding {}, but only {} descriptors are \ available", written_count, binding, available_count, ), Self::ImageView2dFrom3d { binding, index } => write!( f, "tried to write an image view to binding {} index {} with a 2D type and a 3D \ underlying image", binding, index, ), Self::ImageViewDepthAndStencil { binding, index } => write!( f, "tried to write an image view to binding {} index {} that has both the `depth` and \ `stencil` aspects", binding, index, ), Self::ImageViewHasSamplerYcbcrConversion { binding, index } => write!( f, "tried to write an image view to binding {} index {} with an attached sampler \ YCbCr conversion to binding that does not support it", binding, index, ), Self::ImageViewIsArrayed { binding, index } => write!( f, "tried to write an image view of an arrayed type to binding {} index {}, but this \ binding has a descriptor type that does not support arrayed image views", binding, index, ), Self::ImageViewIncompatibleSampler { binding, index, .. } => write!( f, "tried to write an image view to binding {} index {}, that was not compatible with \ the sampler that was provided as part of the update or immutably in the layout", binding, index, ), Self::ImageViewNotIdentitySwizzled { binding, index } => write!( f, "tried to write an image view with non-identity swizzling to binding {} index {}, \ but this binding has a descriptor type that requires it to be identity swizzled", binding, index, ), Self::IncompatibleElementType { binding, provided_element_type, allowed_element_types, } => write!( f, "tried to write a resource to binding {} whose type ({}) was not one of the \ resource types allowed for the descriptor type (", binding, provided_element_type, ) .and_then(|_| { let mut first = true; for elem_type in *allowed_element_types { if first { write!(f, "{}", elem_type)?; first = false; } else { write!(f, ", {}", elem_type)?; } } Ok(()) }) .and_then(|_| write!(f, ") that can be bound to this buffer")), Self::InvalidBinding { binding } => { write!(f, "tried to write to a nonexistent binding {}", binding,) } Self::MissingUsage { binding, index, usage, } => write!( f, "tried to write a resource to binding {} index {} that did not have the required \ usage {} enabled", binding, index, usage, ), Self::RangeOutOfBufferBounds { binding, index, range_end, buffer_size, } => write!( f, "the end of the provided `range` for the buffer at binding {} index {} ({:?}) is larger than the size of the buffer ({})", binding, index, range_end, buffer_size, ), Self::SamplerHasSamplerYcbcrConversion { binding, index } => write!( f, "tried to write a sampler to binding {} index {} that has an attached sampler \ YCbCr conversion", binding, index, ), } } }