// Copyright (c) 2016 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. //! Description of a single descriptor. //! //! This module contains traits and structs related to describing a single descriptor. A descriptor //! is a slot where you can bind a buffer or an image so that it can be accessed from your shaders. //! In order to specify which buffer or image to bind to a descriptor, see the `descriptor_set` //! module. //! //! There are four different kinds of descriptors that give access to buffers: //! //! - Uniform texel buffers. Gives read-only access to the content of a buffer. Only supports //! certain buffer formats. //! - Storage texel buffers. Gives read and/or write access to the content of a buffer. Only //! supports certain buffer formats. Less restrictive but sometimes slower than uniform texel //! buffers. //! - Uniform buffers. Gives read-only access to the content of a buffer. Less restrictive but //! sometimes slower than uniform texel buffers. //! - Storage buffers. Gives read and/or write access to the content of a buffer. Less restrictive //! but sometimes slower than uniform buffers and storage texel buffers. //! //! There are five different kinds of descriptors related to images: //! //! - Storage images. Gives read and/or write access to individual pixels in an image. The image //! cannot be sampled. In other words, you have exactly specify which pixel to read or write. //! - Sampled images. Gives read-only access to an image. Before you can use a sampled image in a //! a shader, you have to combine it with a sampler (see below). The sampler describes how //! reading the image will behave. //! - Samplers. Doesn't contain an image but a sampler object that describes how an image will be //! accessed. This is meant to be combined with a sampled image (see above). //! - Combined image and sampler. Similar to a sampled image, but also directly includes the //! sampler which indicates how the sampling is done. //! - Input attachments. The fastest but also most restrictive access to images. Must be integrated //! in a render pass. Can only give access to the same pixel as the one you're processing. //! use crate::format::Format; use crate::image::view::ImageViewType; use crate::pipeline::shader::ShaderStages; use crate::pipeline::shader::ShaderStagesSupersetError; use crate::sync::AccessFlags; use crate::sync::PipelineStages; use smallvec::SmallVec; use std::cmp; use std::error; use std::fmt; #[derive(Clone, Debug, Default)] pub struct DescriptorSetDesc { descriptors: SmallVec<[Option; 32]>, } impl DescriptorSetDesc { /// Builds a new `DescriptorSetDesc` with the given descriptors. /// /// The descriptors must be passed in the order of the bindings. In order words, descriptor /// at bind point 0 first, then descriptor at bind point 1, and so on. If a binding must remain /// empty, you can make the iterator yield `None` for an element. #[inline] pub fn new(descriptors: I) -> DescriptorSetDesc where I: IntoIterator>, { DescriptorSetDesc { descriptors: descriptors.into_iter().collect(), } } /// Builds a new empty `DescriptorSetDesc`. #[inline] pub fn empty() -> DescriptorSetDesc { DescriptorSetDesc { descriptors: SmallVec::new(), } } /// Returns the descriptors in the set. pub fn bindings(&self) -> &[Option] { &self.descriptors } /// Returns the descriptor with the given binding number, or `None` if the binding is empty. #[inline] pub fn descriptor(&self, num: usize) -> Option<&DescriptorDesc> { self.descriptors.get(num).and_then(|b| b.as_ref()) } /// Builds the union of this layout description and another. #[inline] pub fn union( first: &DescriptorSetDesc, second: &DescriptorSetDesc, ) -> Result { let num_bindings = cmp::max(first.descriptors.len(), second.descriptors.len()); let descriptors = (0..num_bindings) .map(|binding_num| { DescriptorDesc::union( first .descriptors .get(binding_num) .map(|desc| desc.as_ref()) .flatten(), second .descriptors .get(binding_num) .map(|desc| desc.as_ref()) .flatten(), ) }) .collect::>()?; Ok(DescriptorSetDesc { descriptors }) } /// Builds the union of multiple descriptor sets. pub fn union_multiple( first: &[DescriptorSetDesc], second: &[DescriptorSetDesc], ) -> Result, ()> { // Ewwwwwww let empty = DescriptorSetDesc::empty(); let num_sets = cmp::max(first.len(), second.len()); (0..num_sets) .map(|set_num| { Ok(DescriptorSetDesc::union( first.get(set_num).unwrap_or_else(|| &empty), second.get(set_num).unwrap_or_else(|| &empty), )?) }) .collect() } /// Transforms a `DescriptorSetDesc`. /// /// Used to adjust automatically inferred `DescriptorSetDesc`s with information that cannot be inferred. pub fn tweak(&mut self, dynamic_buffers: I) where I: IntoIterator, { for binding_num in dynamic_buffers { debug_assert!( self.descriptor(binding_num) .map_or(false, |desc| match desc.ty { DescriptorDescTy::Buffer(_) => true, _ => false, }), "tried to make the non-buffer descriptor at binding {} a dynamic buffer", binding_num ); let binding = self .descriptors .get_mut(binding_num) .and_then(|b| b.as_mut()); if let Some(desc) = binding { if let DescriptorDescTy::Buffer(ref buffer_desc) = desc.ty { desc.ty = DescriptorDescTy::Buffer(DescriptorBufferDesc { dynamic: Some(true), ..*buffer_desc }); } } } } pub fn tweak_multiple(sets: &mut [DescriptorSetDesc], dynamic_buffers: I) where I: IntoIterator, { for (set_num, binding_num) in dynamic_buffers { debug_assert!( set_num < sets.len(), "tried to make a dynamic buffer in the nonexistent set {}", set_num, ); sets.get_mut(set_num).map(|set| set.tweak([binding_num])); } } /// Returns whether `self` is a superset of `other`. pub fn ensure_superset_of( &self, other: &DescriptorSetDesc, ) -> Result<(), DescriptorSetDescSupersetError> { if self.descriptors.len() < other.descriptors.len() { return Err(DescriptorSetDescSupersetError::DescriptorsCountMismatch { self_num: self.descriptors.len() as u32, other_num: other.descriptors.len() as u32, }); } for binding_num in 0..other.descriptors.len() { let self_desc = self.descriptor(binding_num); let other_desc = self.descriptor(binding_num); match (self_desc, other_desc) { (Some(mine), Some(other)) => { if let Err(err) = mine.ensure_superset_of(&other) { return Err(DescriptorSetDescSupersetError::IncompatibleDescriptors { error: err, binding_num: binding_num as u32, }); } } (None, Some(_)) => { return Err(DescriptorSetDescSupersetError::ExpectedEmptyDescriptor { binding_num: binding_num as u32, }) } _ => (), } } Ok(()) } } impl From for DescriptorSetDesc where I: IntoIterator>, { #[inline] fn from(val: I) -> Self { DescriptorSetDesc { descriptors: val.into_iter().collect(), } } } /// Contains the exact description of a single descriptor. /// /// > **Note**: You are free to fill a `DescriptorDesc` struct the way you want, but its validity /// > will be checked when you create a pipeline layout, a descriptor set, or when you try to bind /// > a descriptor set. // TODO: add example #[derive(Clone, Debug, PartialEq)] pub struct DescriptorDesc { /// Describes the content and layout of each array element of a descriptor. pub ty: DescriptorDescTy, /// How many array elements this descriptor is made of. The value 0 is invalid and may trigger /// a panic depending on the situation. pub array_count: u32, /// Which shader stages are going to access this descriptor. pub stages: ShaderStages, /// True if the attachment is only ever read by the shader. False if it is also written. pub readonly: bool, } impl DescriptorDesc { /// Checks whether we are a superset of another descriptor. /// /// Returns true if `self` is the same descriptor as `other`, or if `self` is the same as /// `other` but with a larger array elements count and/or more shader stages. /// ///# Example ///``` ///use vulkano::descriptor_set::layout::DescriptorDesc; ///use vulkano::descriptor_set::layout::DescriptorDescTy::*; ///use vulkano::pipeline::shader::ShaderStages; /// ///let desc_super = DescriptorDesc{ ty: Sampler, array_count: 2, stages: ShaderStages{ /// vertex: true, /// tessellation_control: true, /// tessellation_evaluation: true, /// geometry: true, /// fragment: true, /// compute: true ///}, readonly: false }; ///let desc_sub = DescriptorDesc{ ty: Sampler, array_count: 1, stages: ShaderStages{ /// vertex: true, /// tessellation_control: false, /// tessellation_evaluation: false, /// geometry: false, /// fragment: true, /// compute: false ///}, readonly: true }; /// ///assert_eq!(desc_super.ensure_superset_of(&desc_sub).unwrap(), ()); /// ///``` #[inline] pub fn ensure_superset_of( &self, other: &DescriptorDesc, ) -> Result<(), DescriptorDescSupersetError> { self.ty.ensure_superset_of(&other.ty)?; self.stages.ensure_superset_of(&other.stages)?; if self.array_count < other.array_count { return Err(DescriptorDescSupersetError::ArrayTooSmall { len: self.array_count, required: other.array_count, }); } if self.readonly && !other.readonly { return Err(DescriptorDescSupersetError::MutabilityRequired); } Ok(()) } /// Builds a `DescriptorDesc` that is the union of `self` and `other`, if possible. /// /// The returned value will be a superset of both `self` and `other`, or `None` if both were /// `None`. /// /// `Err` is returned if the descriptors are not compatible. /// ///# Example ///``` ///use vulkano::descriptor_set::layout::DescriptorDesc; ///use vulkano::descriptor_set::layout::DescriptorDescTy::*; ///use vulkano::pipeline::shader::ShaderStages; /// ///let desc_part1 = DescriptorDesc{ ty: Sampler, array_count: 2, stages: ShaderStages{ /// vertex: true, /// tessellation_control: true, /// tessellation_evaluation: false, /// geometry: true, /// fragment: false, /// compute: true ///}, readonly: false }; /// ///let desc_part2 = DescriptorDesc{ ty: Sampler, array_count: 1, stages: ShaderStages{ /// vertex: true, /// tessellation_control: false, /// tessellation_evaluation: true, /// geometry: false, /// fragment: true, /// compute: true ///}, readonly: true }; /// ///let desc_union = DescriptorDesc{ ty: Sampler, array_count: 2, stages: ShaderStages{ /// vertex: true, /// tessellation_control: true, /// tessellation_evaluation: true, /// geometry: true, /// fragment: true, /// compute: true ///}, readonly: false }; /// ///assert_eq!(DescriptorDesc::union(Some(&desc_part1), Some(&desc_part2)), Ok(Some(desc_union))); ///``` #[inline] pub fn union( first: Option<&DescriptorDesc>, second: Option<&DescriptorDesc>, ) -> Result, ()> { if let (Some(first), Some(second)) = (first, second) { if first.ty != second.ty { return Err(()); } Ok(Some(DescriptorDesc { ty: first.ty.clone(), array_count: cmp::max(first.array_count, second.array_count), stages: first.stages | second.stages, readonly: first.readonly && second.readonly, })) } else { Ok(first.or(second).cloned()) } } /// Returns the pipeline stages and access flags corresponding to the usage of this descriptor. /// /// # Panic /// /// Panics if the type is `Sampler`. /// pub fn pipeline_stages_and_access(&self) -> (PipelineStages, AccessFlags) { let stages: PipelineStages = self.stages.into(); let access = match self.ty { DescriptorDescTy::Sampler => panic!(), DescriptorDescTy::CombinedImageSampler(_) | DescriptorDescTy::Image(_) => AccessFlags { shader_read: true, shader_write: !self.readonly, ..AccessFlags::none() }, DescriptorDescTy::TexelBuffer { .. } => AccessFlags { shader_read: true, shader_write: !self.readonly, ..AccessFlags::none() }, DescriptorDescTy::InputAttachment { .. } => AccessFlags { input_attachment_read: true, ..AccessFlags::none() }, DescriptorDescTy::Buffer(ref buf) => { if buf.storage { AccessFlags { shader_read: true, shader_write: !self.readonly, ..AccessFlags::none() } } else { AccessFlags { uniform_read: true, ..AccessFlags::none() } } } }; (stages, access) } } /// Describes the content and layout of each array element of a descriptor. #[derive(Debug, Clone, PartialEq, Eq)] pub enum DescriptorDescTy { Sampler, // TODO: the sampler has some restrictions as well CombinedImageSampler(DescriptorImageDesc), // TODO: the sampler has some restrictions as well Image(DescriptorImageDesc), TexelBuffer { /// If `true`, this describes a storage texel buffer. storage: bool, /// The format of the content, or `None` if the format is unknown. Depending on the /// context, it may be invalid to have a `None` value here. If the format is `Some`, only /// buffer views that have this exact format can be attached to this descriptor. format: Option, }, InputAttachment { /// If `true`, the input attachment is multisampled. Only multisampled images can be /// attached to this descriptor. If `false`, only single-sampled images can be attached. multisampled: bool, array_layers: DescriptorImageDescArray, }, Buffer(DescriptorBufferDesc), } impl DescriptorDescTy { /// Returns the type of descriptor. // TODO: add example pub fn ty(&self) -> DescriptorType { match *self { DescriptorDescTy::Sampler => DescriptorType::Sampler, DescriptorDescTy::CombinedImageSampler(_) => DescriptorType::CombinedImageSampler, DescriptorDescTy::Image(ref desc) => { if desc.sampled { DescriptorType::SampledImage } else { DescriptorType::StorageImage } } DescriptorDescTy::InputAttachment { .. } => DescriptorType::InputAttachment, DescriptorDescTy::Buffer(ref desc) => { let dynamic = desc.dynamic.unwrap_or(false); match (desc.storage, dynamic) { (false, false) => DescriptorType::UniformBuffer, (true, false) => DescriptorType::StorageBuffer, (false, true) => DescriptorType::UniformBufferDynamic, (true, true) => DescriptorType::StorageBufferDynamic, } } DescriptorDescTy::TexelBuffer { storage, .. } => { if storage { DescriptorType::StorageTexelBuffer } else { DescriptorType::UniformTexelBuffer } } } } /// Checks whether we are a superset of another descriptor type. // TODO: add example #[inline] pub fn ensure_superset_of( &self, other: &DescriptorDescTy, ) -> Result<(), DescriptorDescSupersetError> { match (self, other) { (&DescriptorDescTy::Sampler, &DescriptorDescTy::Sampler) => Ok(()), ( &DescriptorDescTy::CombinedImageSampler(ref me), &DescriptorDescTy::CombinedImageSampler(ref other), ) => me.ensure_superset_of(other), (&DescriptorDescTy::Image(ref me), &DescriptorDescTy::Image(ref other)) => { me.ensure_superset_of(other) } ( &DescriptorDescTy::InputAttachment { multisampled: me_multisampled, array_layers: me_array_layers, }, &DescriptorDescTy::InputAttachment { multisampled: other_multisampled, array_layers: other_array_layers, }, ) => { if me_multisampled != other_multisampled { return Err(DescriptorDescSupersetError::MultisampledMismatch { provided: me_multisampled, expected: other_multisampled, }); } if me_array_layers != other_array_layers { return Err(DescriptorDescSupersetError::IncompatibleArrayLayers { provided: me_array_layers, required: other_array_layers, }); } Ok(()) } (&DescriptorDescTy::Buffer(ref me), &DescriptorDescTy::Buffer(ref other)) => { if me.storage != other.storage { return Err(DescriptorDescSupersetError::TypeMismatch); } match (me.dynamic, other.dynamic) { (Some(_), None) => Ok(()), (Some(m), Some(o)) => { if m == o { Ok(()) } else { Err(DescriptorDescSupersetError::TypeMismatch) } } (None, None) => Ok(()), (None, Some(_)) => Err(DescriptorDescSupersetError::TypeMismatch), } } ( &DescriptorDescTy::TexelBuffer { storage: me_storage, format: me_format, }, &DescriptorDescTy::TexelBuffer { storage: other_storage, format: other_format, }, ) => { if me_storage != other_storage { return Err(DescriptorDescSupersetError::TypeMismatch); } match (me_format, other_format) { (Some(_), None) => Ok(()), (Some(m), Some(o)) => { if m == o { Ok(()) } else { Err(DescriptorDescSupersetError::FormatMismatch { provided: Some(m), expected: o, }) } } (None, None) => Ok(()), (None, Some(a)) => Err(DescriptorDescSupersetError::FormatMismatch { provided: Some(a), expected: a, }), } } // Any other combination is invalid. _ => Err(DescriptorDescSupersetError::TypeMismatch), } } } /// Additional description for descriptors that contain images. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct DescriptorImageDesc { /// If `true`, the image can be sampled by the shader. Only images that were created with the /// `sampled` usage can be attached to the descriptor. pub sampled: bool, /// The kind of image: one-dimensional, two-dimensional, three-dimensional, or cube. pub dimensions: DescriptorImageDescDimensions, /// The format of the image, or `None` if the format is unknown. If `Some`, only images with /// exactly that format can be attached. pub format: Option, /// True if the image is multisampled. pub multisampled: bool, /// Whether the descriptor contains one or more array layers of an image. pub array_layers: DescriptorImageDescArray, } impl DescriptorImageDesc { /// Checks whether we are a superset of another image. // TODO: add example #[inline] pub fn ensure_superset_of( &self, other: &DescriptorImageDesc, ) -> Result<(), DescriptorDescSupersetError> { if self.dimensions != other.dimensions { return Err(DescriptorDescSupersetError::DimensionsMismatch { provided: self.dimensions, expected: other.dimensions, }); } if self.multisampled != other.multisampled { return Err(DescriptorDescSupersetError::MultisampledMismatch { provided: self.multisampled, expected: other.multisampled, }); } match (self.format, other.format) { (Some(a), Some(b)) => { if a != b { return Err(DescriptorDescSupersetError::FormatMismatch { provided: Some(a), expected: b, }); } } (Some(_), None) => (), (None, None) => (), (None, Some(a)) => { return Err(DescriptorDescSupersetError::FormatMismatch { provided: None, expected: a, }); } }; match (self.array_layers, other.array_layers) { (DescriptorImageDescArray::NonArrayed, DescriptorImageDescArray::NonArrayed) => (), ( DescriptorImageDescArray::Arrayed { max_layers: my_max }, DescriptorImageDescArray::Arrayed { max_layers: other_max, }, ) => { match (my_max, other_max) { (Some(m), Some(o)) => { if m < o { return Err(DescriptorDescSupersetError::IncompatibleArrayLayers { provided: DescriptorImageDescArray::Arrayed { max_layers: my_max }, required: DescriptorImageDescArray::Arrayed { max_layers: other_max, }, }); } } (Some(_), None) => (), (None, Some(m)) => { return Err(DescriptorDescSupersetError::IncompatibleArrayLayers { provided: DescriptorImageDescArray::Arrayed { max_layers: my_max }, required: DescriptorImageDescArray::Arrayed { max_layers: other_max, }, }); } (None, None) => (), // TODO: is this correct? }; } (a, b) => { return Err(DescriptorDescSupersetError::IncompatibleArrayLayers { provided: a, required: b, }) } }; Ok(()) } } // TODO: documentation #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum DescriptorImageDescArray { NonArrayed, Arrayed { max_layers: Option }, } // TODO: documentation #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum DescriptorImageDescDimensions { OneDimensional, TwoDimensional, ThreeDimensional, Cube, } impl DescriptorImageDescDimensions { /// Builds the `DescriptorImageDescDimensions` that corresponds to the view type. #[inline] pub fn from_image_view_type(ty: ImageViewType) -> DescriptorImageDescDimensions { match ty { ImageViewType::Dim1d => DescriptorImageDescDimensions::OneDimensional, ImageViewType::Dim1dArray => DescriptorImageDescDimensions::OneDimensional, ImageViewType::Dim2d => DescriptorImageDescDimensions::TwoDimensional, ImageViewType::Dim2dArray => DescriptorImageDescDimensions::TwoDimensional, ImageViewType::Dim3d => DescriptorImageDescDimensions::ThreeDimensional, ImageViewType::Cubemap => DescriptorImageDescDimensions::Cube, ImageViewType::CubemapArray => DescriptorImageDescDimensions::Cube, } } } /// Additional description for descriptors that contain buffers. #[derive(Debug, Clone, PartialEq, Eq)] pub struct DescriptorBufferDesc { /// If `true`, this buffer is a dynamic buffer. Assumes false if `None`. pub dynamic: Option, /// If `true`, this buffer is a storage buffer. pub storage: bool, } /// Describes what kind of resource may later be bound to a descriptor. /// /// This is mostly the same as a `DescriptorDescTy` but with less precise information. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[repr(i32)] pub enum DescriptorType { Sampler = ash::vk::DescriptorType::SAMPLER.as_raw(), CombinedImageSampler = ash::vk::DescriptorType::COMBINED_IMAGE_SAMPLER.as_raw(), SampledImage = ash::vk::DescriptorType::SAMPLED_IMAGE.as_raw(), StorageImage = ash::vk::DescriptorType::STORAGE_IMAGE.as_raw(), UniformTexelBuffer = ash::vk::DescriptorType::UNIFORM_TEXEL_BUFFER.as_raw(), StorageTexelBuffer = ash::vk::DescriptorType::STORAGE_TEXEL_BUFFER.as_raw(), UniformBuffer = ash::vk::DescriptorType::UNIFORM_BUFFER.as_raw(), StorageBuffer = ash::vk::DescriptorType::STORAGE_BUFFER.as_raw(), UniformBufferDynamic = ash::vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC.as_raw(), StorageBufferDynamic = ash::vk::DescriptorType::STORAGE_BUFFER_DYNAMIC.as_raw(), InputAttachment = ash::vk::DescriptorType::INPUT_ATTACHMENT.as_raw(), } impl From for ash::vk::DescriptorType { #[inline] fn from(val: DescriptorType) -> Self { Self::from_raw(val as i32) } } /// Error when checking whether a descriptor set is a superset of another one. #[derive(Debug, Clone, PartialEq, Eq)] pub enum DescriptorSetDescSupersetError { /// There are more descriptors in the child than in the parent layout. DescriptorsCountMismatch { self_num: u32, other_num: u32 }, /// Expected an empty descriptor, but got something instead. ExpectedEmptyDescriptor { binding_num: u32 }, /// Two descriptors are incompatible. IncompatibleDescriptors { error: DescriptorDescSupersetError, binding_num: u32, }, } impl error::Error for DescriptorSetDescSupersetError { #[inline] fn source(&self) -> Option<&(dyn error::Error + 'static)> { match *self { DescriptorSetDescSupersetError::IncompatibleDescriptors { ref error, .. } => { Some(error) } _ => None, } } } impl fmt::Display for DescriptorSetDescSupersetError { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!( fmt, "{}", match *self { DescriptorSetDescSupersetError::DescriptorsCountMismatch { .. } => { "there are more descriptors in the child than in the parent layout" } DescriptorSetDescSupersetError::ExpectedEmptyDescriptor { .. } => { "expected an empty descriptor, but got something instead" } DescriptorSetDescSupersetError::IncompatibleDescriptors { .. } => { "two descriptors are incompatible" } } ) } } /// Error when checking whether a descriptor is a superset of another one. #[derive(Debug, Clone, PartialEq, Eq)] pub enum DescriptorDescSupersetError { /// The number of array elements of the descriptor is smaller than expected. ArrayTooSmall { len: u32, required: u32, }, DimensionsMismatch { provided: DescriptorImageDescDimensions, expected: DescriptorImageDescDimensions, }, FormatMismatch { provided: Option, expected: Format, }, IncompatibleArrayLayers { provided: DescriptorImageDescArray, required: DescriptorImageDescArray, }, MultisampledMismatch { provided: bool, expected: bool, }, /// The descriptor is marked as read-only, but the other is not. MutabilityRequired, /// The shader stages are not a superset of one another. ShaderStagesNotSuperset, /// The descriptor type doesn't match the type of the other descriptor. TypeMismatch, } impl error::Error for DescriptorDescSupersetError {} impl fmt::Display for DescriptorDescSupersetError { #[inline] fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!( fmt, "{}", match *self { DescriptorDescSupersetError::ArrayTooSmall { .. } => { "the number of array elements of the descriptor is smaller than expected" } DescriptorDescSupersetError::TypeMismatch => { "the descriptor type doesn't match the type of the other descriptor" } DescriptorDescSupersetError::MutabilityRequired => { "the descriptor is marked as read-only, but the other is not" } DescriptorDescSupersetError::ShaderStagesNotSuperset => { "the shader stages are not a superset of one another" } DescriptorDescSupersetError::DimensionsMismatch { .. } => { "mismatch between the dimensions of the two descriptors" } DescriptorDescSupersetError::FormatMismatch { .. } => { "mismatch between the format of the two descriptors" } DescriptorDescSupersetError::MultisampledMismatch { .. } => { "mismatch between whether the descriptors are multisampled" } DescriptorDescSupersetError::IncompatibleArrayLayers { .. } => { "the array layers of the descriptors aren't compatible" } } ) } } impl From for DescriptorDescSupersetError { #[inline] fn from(err: ShaderStagesSupersetError) -> DescriptorDescSupersetError { match err { ShaderStagesSupersetError::NotSuperset => { DescriptorDescSupersetError::ShaderStagesNotSuperset } } } }