// Copyright (c) 2021 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. //! Describes the layout of all descriptors within a descriptor set. //! //! When creating a new descriptor set, you must provide a *layout* object to create it from. use crate::{ device::{Device, DeviceOwned}, macros::{impl_id_counter, vulkan_enum}, sampler::Sampler, shader::{DescriptorBindingRequirements, ShaderStages}, OomError, RequirementNotMet, RequiresOneOf, Version, VulkanError, VulkanObject, }; use ahash::HashMap; use std::{ collections::BTreeMap, error::Error, fmt::{Display, Error as FmtError, Formatter}, mem::MaybeUninit, num::NonZeroU64, ptr, sync::Arc, }; /// Describes to the Vulkan implementation the layout of all descriptors within a descriptor set. #[derive(Debug)] pub struct DescriptorSetLayout { handle: ash::vk::DescriptorSetLayout, device: Arc, id: NonZeroU64, bindings: BTreeMap, push_descriptor: bool, descriptor_counts: HashMap, } impl DescriptorSetLayout { /// Creates a new `DescriptorSetLayout`. #[inline] pub fn new( device: Arc, mut create_info: DescriptorSetLayoutCreateInfo, ) -> Result, DescriptorSetLayoutCreationError> { let descriptor_counts = Self::validate(&device, &mut create_info)?; let handle = unsafe { Self::create(&device, &create_info)? }; let DescriptorSetLayoutCreateInfo { bindings, push_descriptor, _ne: _, } = create_info; Ok(Arc::new(DescriptorSetLayout { handle, device, id: Self::next_id(), bindings, push_descriptor, descriptor_counts, })) } /// Creates a new `DescriptorSetLayout` from a raw object handle. /// /// # Safety /// /// - `handle` must be a valid Vulkan object handle created from `device`. /// - `create_info` must match the info used to create the object. #[inline] pub unsafe fn from_handle( device: Arc, handle: ash::vk::DescriptorSetLayout, create_info: DescriptorSetLayoutCreateInfo, ) -> Arc { let DescriptorSetLayoutCreateInfo { bindings, push_descriptor, _ne: _, } = create_info; let mut descriptor_counts = HashMap::default(); for binding in bindings.values() { if binding.descriptor_count != 0 { *descriptor_counts .entry(binding.descriptor_type) .or_default() += binding.descriptor_count; } } Arc::new(DescriptorSetLayout { handle, device, id: Self::next_id(), bindings, push_descriptor, descriptor_counts, }) } fn validate( device: &Device, create_info: &mut DescriptorSetLayoutCreateInfo, ) -> Result, DescriptorSetLayoutCreationError> { let &mut DescriptorSetLayoutCreateInfo { ref bindings, push_descriptor, _ne: _, } = create_info; let mut descriptor_counts = HashMap::default(); if push_descriptor { if !device.enabled_extensions().khr_push_descriptor { return Err(DescriptorSetLayoutCreationError::RequirementNotMet { required_for: "`create_info.push_descriptor` is set", requires_one_of: RequiresOneOf { device_extensions: &["khr_push_descriptor"], ..Default::default() }, }); } } let highest_binding_num = bindings.keys().copied().next_back(); for (&binding_num, binding) in bindings.iter() { let &DescriptorSetLayoutBinding { descriptor_type, descriptor_count, variable_descriptor_count, stages, ref immutable_samplers, _ne: _, } = binding; // VUID-VkDescriptorSetLayoutBinding-descriptorType-parameter descriptor_type.validate_device(device)?; if descriptor_count != 0 { // VUID-VkDescriptorSetLayoutBinding-descriptorCount-00283 stages.validate_device(device)?; *descriptor_counts.entry(descriptor_type).or_default() += descriptor_count; } if push_descriptor { // VUID-VkDescriptorSetLayoutCreateInfo-flags-00280 if matches!( descriptor_type, DescriptorType::StorageBufferDynamic | DescriptorType::UniformBufferDynamic ) { return Err( DescriptorSetLayoutCreationError::PushDescriptorDescriptorTypeIncompatible { binding_num, }, ); } // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-flags-03003 if variable_descriptor_count { return Err( DescriptorSetLayoutCreationError::PushDescriptorVariableDescriptorCount { binding_num, }, ); } } if !immutable_samplers.is_empty() { if immutable_samplers .iter() .any(|sampler| sampler.sampler_ycbcr_conversion().is_some()) { if !matches!(descriptor_type, DescriptorType::CombinedImageSampler) { return Err( DescriptorSetLayoutCreationError::ImmutableSamplersDescriptorTypeIncompatible { binding_num, }, ); } } else { if !matches!( descriptor_type, DescriptorType::Sampler | DescriptorType::CombinedImageSampler ) { return Err( DescriptorSetLayoutCreationError::ImmutableSamplersDescriptorTypeIncompatible { binding_num, }, ); } } // VUID-VkDescriptorSetLayoutBinding-descriptorType-00282 if descriptor_count != immutable_samplers.len() as u32 { return Err( DescriptorSetLayoutCreationError::ImmutableSamplersCountMismatch { binding_num, sampler_count: immutable_samplers.len() as u32, descriptor_count, }, ); } } // VUID-VkDescriptorSetLayoutBinding-descriptorType-01510 // If descriptorType is VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT and descriptorCount is not 0, then stageFlags must be 0 or VK_SHADER_STAGE_FRAGMENT_BIT if variable_descriptor_count { // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-descriptorBindingVariableDescriptorCount-03014 if !device .enabled_features() .descriptor_binding_variable_descriptor_count { return Err(DescriptorSetLayoutCreationError::RequirementNotMet { required_for: "`create_info.bindings` has an element where \ `variable_descriptor_count` is set", requires_one_of: RequiresOneOf { features: &["descriptor_binding_variable_descriptor_count"], ..Default::default() }, }); } // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03004 if Some(binding_num) != highest_binding_num { return Err( DescriptorSetLayoutCreationError::VariableDescriptorCountBindingNotHighest { binding_num, highest_binding_num: highest_binding_num.unwrap(), }, ); } // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-pBindingFlags-03015 if matches!( descriptor_type, DescriptorType::UniformBufferDynamic | DescriptorType::StorageBufferDynamic ) { return Err( DescriptorSetLayoutCreationError::VariableDescriptorCountDescriptorTypeIncompatible { binding_num, }, ); } } } // VUID-VkDescriptorSetLayoutCreateInfo-flags-00281 if push_descriptor && descriptor_counts.values().copied().sum::() > device .physical_device() .properties() .max_push_descriptors .unwrap_or(0) { return Err( DescriptorSetLayoutCreationError::MaxPushDescriptorsExceeded { provided: descriptor_counts.values().copied().sum(), max_supported: device .physical_device() .properties() .max_push_descriptors .unwrap_or(0), }, ); } Ok(descriptor_counts) } unsafe fn create( device: &Device, create_info: &DescriptorSetLayoutCreateInfo, ) -> Result { let &DescriptorSetLayoutCreateInfo { ref bindings, push_descriptor, _ne: _, } = create_info; let mut bindings_vk = Vec::with_capacity(bindings.len()); let mut binding_flags_vk = Vec::with_capacity(bindings.len()); let mut immutable_samplers_vk: Vec> = Vec::new(); // only to keep the arrays of handles alive let mut flags = ash::vk::DescriptorSetLayoutCreateFlags::empty(); if push_descriptor { flags |= ash::vk::DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR_KHR; } for (&binding_num, binding) in bindings.iter() { let mut binding_flags = ash::vk::DescriptorBindingFlags::empty(); let p_immutable_samplers = if !binding.immutable_samplers.is_empty() { // VUID-VkDescriptorSetLayoutBinding-descriptorType-00282 let sampler_handles = binding .immutable_samplers .iter() .map(|s| s.handle()) .collect::>() .into_boxed_slice(); let p_immutable_samplers = sampler_handles.as_ptr(); immutable_samplers_vk.push(sampler_handles); p_immutable_samplers } else { ptr::null() }; if binding.variable_descriptor_count { binding_flags |= ash::vk::DescriptorBindingFlags::VARIABLE_DESCRIPTOR_COUNT; } // VUID-VkDescriptorSetLayoutCreateInfo-binding-00279 // Guaranteed by BTreeMap bindings_vk.push(ash::vk::DescriptorSetLayoutBinding { binding: binding_num, descriptor_type: binding.descriptor_type.into(), descriptor_count: binding.descriptor_count, stage_flags: binding.stages.into(), p_immutable_samplers, }); binding_flags_vk.push(binding_flags); } let mut binding_flags_create_info = if device.api_version() >= Version::V1_2 || device.enabled_extensions().ext_descriptor_indexing { Some(ash::vk::DescriptorSetLayoutBindingFlagsCreateInfo { // VUID-VkDescriptorSetLayoutBindingFlagsCreateInfo-bindingCount-03002 binding_count: binding_flags_vk.len() as u32, p_binding_flags: binding_flags_vk.as_ptr(), ..Default::default() }) } else { None }; let mut create_info = ash::vk::DescriptorSetLayoutCreateInfo { flags, binding_count: bindings_vk.len() as u32, p_bindings: bindings_vk.as_ptr(), ..Default::default() }; if let Some(binding_flags_create_info) = binding_flags_create_info.as_mut() { binding_flags_create_info.p_next = create_info.p_next; create_info.p_next = binding_flags_create_info as *const _ as *const _; } let handle = { let fns = device.fns(); let mut output = MaybeUninit::uninit(); (fns.v1_0.create_descriptor_set_layout)( device.handle(), &create_info, ptr::null(), output.as_mut_ptr(), ) .result() .map_err(VulkanError::from)?; output.assume_init() }; Ok(handle) } pub(crate) fn id(&self) -> NonZeroU64 { self.id } /// Returns the bindings of the descriptor set layout. #[inline] pub fn bindings(&self) -> &BTreeMap { &self.bindings } /// Returns whether the descriptor set layout is for push descriptors or regular descriptor /// sets. #[inline] pub fn push_descriptor(&self) -> bool { self.push_descriptor } /// Returns the number of descriptors of each type. /// /// The map is guaranteed to not contain any elements with a count of `0`. #[inline] pub fn descriptor_counts(&self) -> &HashMap { &self.descriptor_counts } /// If the highest-numbered binding has a variable count, returns its `descriptor_count`. /// Otherwise returns `0`. #[inline] pub fn variable_descriptor_count(&self) -> u32 { self.bindings .values() .next_back() .map(|binding| { if binding.variable_descriptor_count { binding.descriptor_count } else { 0 } }) .unwrap_or(0) } /// Returns whether `self` is compatible with `other`. /// /// "Compatible" in this sense is defined by the Vulkan specification under the section /// "Pipeline layout compatibility": either the two are the same descriptor set layout object, /// or they must be identically defined to the Vulkan API. #[inline] pub fn is_compatible_with(&self, other: &DescriptorSetLayout) -> bool { self == other || (self.bindings == other.bindings && self.push_descriptor == other.push_descriptor) } } impl Drop for DescriptorSetLayout { #[inline] fn drop(&mut self) { unsafe { let fns = self.device.fns(); (fns.v1_0.destroy_descriptor_set_layout)( self.device.handle(), self.handle, ptr::null(), ); } } } unsafe impl VulkanObject for DescriptorSetLayout { type Handle = ash::vk::DescriptorSetLayout; #[inline] fn handle(&self) -> Self::Handle { self.handle } } unsafe impl DeviceOwned for DescriptorSetLayout { #[inline] fn device(&self) -> &Arc { &self.device } } impl_id_counter!(DescriptorSetLayout); /// Error related to descriptor set layout. #[derive(Debug, Clone, PartialEq, Eq)] pub enum DescriptorSetLayoutCreationError { /// Out of Memory. OomError(OomError), RequirementNotMet { required_for: &'static str, requires_one_of: RequiresOneOf, }, /// A binding includes immutable samplers but their number differs from `descriptor_count`. ImmutableSamplersCountMismatch { binding_num: u32, sampler_count: u32, descriptor_count: u32, }, /// A binding includes immutable samplers but it has an incompatible `descriptor_type`. ImmutableSamplersDescriptorTypeIncompatible { binding_num: u32 }, /// More descriptors were provided in all bindings than the /// [`max_push_descriptors`](crate::device::Properties::max_push_descriptors) limit. MaxPushDescriptorsExceeded { provided: u32, max_supported: u32 }, /// `push_descriptor` is enabled, but a binding has an incompatible `descriptor_type`. PushDescriptorDescriptorTypeIncompatible { binding_num: u32 }, /// `push_descriptor` is enabled, but a binding has `variable_descriptor_count` enabled. PushDescriptorVariableDescriptorCount { binding_num: u32 }, /// A binding has `variable_descriptor_count` enabled, but it is not the highest-numbered /// binding. VariableDescriptorCountBindingNotHighest { binding_num: u32, highest_binding_num: u32, }, /// A binding has `variable_descriptor_count` enabled, but it has an incompatible /// `descriptor_type`. VariableDescriptorCountDescriptorTypeIncompatible { binding_num: u32 }, } impl From for DescriptorSetLayoutCreationError { fn from(error: VulkanError) -> Self { Self::OomError(error.into()) } } impl Error for DescriptorSetLayoutCreationError {} impl Display for DescriptorSetLayoutCreationError { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::OomError(_) => { write!(f, "out of memory") } Self::RequirementNotMet { required_for, requires_one_of, } => write!( f, "a requirement was not met for: {}; requires one of: {}", required_for, requires_one_of, ), Self::ImmutableSamplersCountMismatch { binding_num, sampler_count, descriptor_count, } => write!( f, "binding {} includes immutable samplers but their number ({}) differs from \ `descriptor_count` ({})", binding_num, sampler_count, descriptor_count, ), Self::ImmutableSamplersDescriptorTypeIncompatible { binding_num } => write!( f, "binding {} includes immutable samplers but it has an incompatible \ `descriptor_type`", binding_num, ), Self::MaxPushDescriptorsExceeded { provided, max_supported, } => write!( f, "more descriptors were provided in all bindings ({}) than the \ `max_push_descriptors` limit ({})", provided, max_supported, ), Self::PushDescriptorDescriptorTypeIncompatible { binding_num } => write!( f, "`push_descriptor` is enabled, but binding {} has an incompatible \ `descriptor_type`", binding_num, ), Self::PushDescriptorVariableDescriptorCount { binding_num } => write!( f, "`push_descriptor` is enabled, but binding {} has `variable_descriptor_count` \ enabled", binding_num, ), Self::VariableDescriptorCountBindingNotHighest { binding_num, highest_binding_num, } => write!( f, "binding {} has `variable_descriptor_count` enabled, but it is not the \ highest-numbered binding ({})", binding_num, highest_binding_num, ), Self::VariableDescriptorCountDescriptorTypeIncompatible { binding_num } => write!( f, "binding {} has `variable_descriptor_count` enabled, but it has an incompatible \ `descriptor_type`", binding_num, ), } } } impl From for DescriptorSetLayoutCreationError { fn from(err: RequirementNotMet) -> Self { Self::RequirementNotMet { required_for: err.required_for, requires_one_of: err.requires_one_of, } } } /// Parameters to create a new `DescriptorSetLayout`. #[derive(Clone, Debug)] pub struct DescriptorSetLayoutCreateInfo { /// The bindings of the desriptor set layout. These are specified according to binding number. /// /// It is generally advisable to keep the binding numbers low. Higher binding numbers may /// use more memory inside Vulkan. /// /// The default value is empty. pub bindings: BTreeMap, /// Whether the descriptor set layout should be created for push descriptors. /// /// If `true`, the layout can only be used for push descriptors, and if `false`, it can only /// be used for regular descriptor sets. /// /// If set to `true`, the /// [`khr_push_descriptor`](crate::device::DeviceExtensions::khr_push_descriptor) extension must /// be enabled on the device, and there are several restrictions: /// - There must be no bindings with a type of [`DescriptorType::UniformBufferDynamic`] /// or [`DescriptorType::StorageBufferDynamic`]. /// - There must be no bindings with `variable_descriptor_count` enabled. /// - The total number of descriptors across all bindings must be less than the /// [`max_push_descriptors`](crate::device::Properties::max_push_descriptors) limit. /// /// The default value is `false`. pub push_descriptor: bool, pub _ne: crate::NonExhaustive, } impl Default for DescriptorSetLayoutCreateInfo { #[inline] fn default() -> Self { Self { bindings: BTreeMap::new(), push_descriptor: false, _ne: crate::NonExhaustive(()), } } } impl DescriptorSetLayoutCreateInfo { /// Builds a list of `DescriptorSetLayoutCreateInfo` from an iterator of /// `DescriptorBindingRequirements` originating from a shader. pub fn from_requirements<'a>( descriptor_requirements: impl IntoIterator< Item = ((u32, u32), &'a DescriptorBindingRequirements), >, ) -> Vec { let mut create_infos: Vec = Vec::new(); for ((set_num, binding_num), reqs) in descriptor_requirements { let set_num = set_num as usize; if set_num >= create_infos.len() { create_infos.resize(set_num + 1, Self::default()); } let bindings = &mut create_infos[set_num].bindings; bindings.insert(binding_num, reqs.into()); } create_infos } } /// A binding in a descriptor set layout. #[derive(Clone, Debug, PartialEq, Eq)] pub struct DescriptorSetLayoutBinding { /// The content and layout of each array element of a binding. /// /// There is no default value. pub descriptor_type: DescriptorType, /// How many descriptors (array elements) this binding is made of. /// /// If the binding is a single element rather than an array, then you must specify `1`. /// /// The default value is `1`. pub descriptor_count: u32, /// Whether the binding has a variable number of descriptors. /// /// If set to `true`, the [`descriptor_binding_variable_descriptor_count`] feature must be /// enabled. The value of `descriptor_count` specifies the maximum number of descriptors /// allowed. /// /// There may only be one binding with a variable count in a descriptor set, and it must be the /// binding with the highest binding number. The `descriptor_type` must not be /// [`DescriptorType::UniformBufferDynamic`] or [`DescriptorType::StorageBufferDynamic`]. /// /// The default value is `false`. /// /// [`descriptor_binding_variable_descriptor_count`]: crate::device::Features::descriptor_binding_variable_descriptor_count pub variable_descriptor_count: bool, /// Which shader stages are going to access the descriptors in this binding. /// /// The default value is [`ShaderStages::empty()`], which must be overridden. pub stages: ShaderStages, /// Samplers that are included as a fixed part of the descriptor set layout. Once bound, they /// do not need to be provided when creating a descriptor set. /// /// The list must be either empty, or contain exactly `descriptor_count` samplers. It can only /// be non-empty if `descriptor_type` is [`DescriptorType::Sampler`] or /// [`DescriptorType::CombinedImageSampler`]. If any of the samplers has an attached sampler /// YCbCr conversion, then only [`DescriptorType::CombinedImageSampler`] is allowed. /// /// The default value is empty. pub immutable_samplers: Vec>, pub _ne: crate::NonExhaustive, } impl DescriptorSetLayoutBinding { /// Returns a `DescriptorSetLayoutBinding` with the given type. #[inline] pub fn descriptor_type(descriptor_type: DescriptorType) -> Self { Self { descriptor_type, descriptor_count: 1, variable_descriptor_count: false, stages: ShaderStages::empty(), immutable_samplers: Vec::new(), _ne: crate::NonExhaustive(()), } } /// Checks whether the descriptor of a pipeline layout `self` is compatible with the /// requirements of a shader `other`. #[inline] pub fn ensure_compatible_with_shader( &self, binding_requirements: &DescriptorBindingRequirements, ) -> Result<(), DescriptorRequirementsNotMet> { let &DescriptorBindingRequirements { ref descriptor_types, descriptor_count, image_format: _, image_multisampled: _, image_scalar_type: _, image_view_type: _, stages, descriptors: _, } = binding_requirements; if !descriptor_types.contains(&self.descriptor_type) { return Err(DescriptorRequirementsNotMet::DescriptorType { required: descriptor_types.clone(), obtained: self.descriptor_type, }); } if let Some(required) = descriptor_count { if self.descriptor_count < required { return Err(DescriptorRequirementsNotMet::DescriptorCount { required, obtained: self.descriptor_count, }); } } if !self.stages.contains(stages) { return Err(DescriptorRequirementsNotMet::ShaderStages { required: stages, obtained: self.stages, }); } Ok(()) } } impl From<&DescriptorBindingRequirements> for DescriptorSetLayoutBinding { #[inline] fn from(reqs: &DescriptorBindingRequirements) -> Self { Self { descriptor_type: reqs.descriptor_types[0], descriptor_count: reqs.descriptor_count.unwrap_or(0), variable_descriptor_count: false, stages: reqs.stages, immutable_samplers: Vec::new(), _ne: crate::NonExhaustive(()), } } } /// Error when checking whether the requirements for a binding have been met. #[derive(Debug, Clone, PartialEq, Eq)] pub enum DescriptorRequirementsNotMet { /// The binding's `descriptor_type` is not one of those required. DescriptorType { required: Vec, obtained: DescriptorType, }, /// The binding's `descriptor_count` is less than what is required. DescriptorCount { required: u32, obtained: u32 }, /// The binding's `stages` does not contain the stages that are required. ShaderStages { required: ShaderStages, obtained: ShaderStages, }, } impl Error for DescriptorRequirementsNotMet {} impl Display for DescriptorRequirementsNotMet { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> { match self { Self::DescriptorType { required, obtained } => write!( f, "the descriptor's type ({:?}) is not one of those required ({:?})", obtained, required, ), Self::DescriptorCount { required, obtained } => write!( f, "the descriptor count ({}) is less than what is required ({})", obtained, required, ), Self::ShaderStages { .. } => write!( f, "the descriptor's shader stages do not contain the stages that are required", ), } } } vulkan_enum! { #[non_exhaustive] /// Describes what kind of resource may later be bound to a descriptor. DescriptorType = DescriptorType(i32); /// Describes how a `SampledImage` descriptor should be read. Sampler = SAMPLER, /// Combines `SampledImage` and `Sampler` in one descriptor. CombinedImageSampler = COMBINED_IMAGE_SAMPLER, /// Gives read-only access to an image via a sampler. The image must be combined with a sampler /// inside the shader. SampledImage = SAMPLED_IMAGE, /// Gives read and/or write access to individual pixels in an image. The image cannot be /// sampled, so you have exactly specify which pixel to read or write. StorageImage = STORAGE_IMAGE, /// Gives read-only access to the content of a buffer, interpreted as an array of texel data. UniformTexelBuffer = UNIFORM_TEXEL_BUFFER, /// Gives read and/or write access to the content of a buffer, interpreted as an array of texel /// data. Less restrictive but sometimes slower than a uniform texel buffer. StorageTexelBuffer = STORAGE_TEXEL_BUFFER, /// Gives read-only access to the content of a buffer, interpreted as a structure. UniformBuffer = UNIFORM_BUFFER, /// Gives read and/or write access to the content of a buffer, interpreted as a structure. Less /// restrictive but sometimes slower than a uniform buffer. StorageBuffer = STORAGE_BUFFER, /// As `UniformBuffer`, but the offset within the buffer is specified at the time the descriptor /// set is bound, rather than when the descriptor set is updated. UniformBufferDynamic = UNIFORM_BUFFER_DYNAMIC, /// As `StorageBuffer`, but the offset within the buffer is specified at the time the descriptor /// set is bound, rather than when the descriptor set is updated. StorageBufferDynamic = STORAGE_BUFFER_DYNAMIC, /// Gives access to an image inside a fragment shader via a render pass. You can only access the /// pixel that is currently being processed by the fragment shader. InputAttachment = INPUT_ATTACHMENT, /* TODO: enable // TODO: document InlineUniformBlock = INLINE_UNIFORM_BLOCK { api_version: V1_3, device_extensions: [ext_inline_uniform_block], },*/ /* TODO: enable // TODO: document AccelerationStructure = ACCELERATION_STRUCTURE_KHR { device_extensions: [khr_acceleration_structure], },*/ /* TODO: enable // TODO: document AccelerationStructureNV = ACCELERATION_STRUCTURE_NV { device_extensions: [nv_ray_tracing], },*/ /* TODO: enable // TODO: document SampleWeightImage = SAMPLE_WEIGHT_IMAGE_QCOM { device_extensions: [qcom_image_processing], },*/ /* TODO: enable // TODO: document BlockMatchImage = BLOCK_MATCH_IMAGE_QCOM { device_extensions: [qcom_image_processing], },*/ /* TODO: enable // TODO: document Mutable = MUTABLE_VALVE { device_extensions: [valve_mutable_descriptor_type], },*/ } #[cfg(test)] mod tests { use crate::{ descriptor_set::layout::{ DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo, DescriptorType, }, shader::ShaderStages, }; use ahash::HashMap; #[test] fn empty() { let (device, _) = gfx_dev_and_queue!(); let _layout = DescriptorSetLayout::new(device, Default::default()); } #[test] fn basic_create() { let (device, _) = gfx_dev_and_queue!(); let sl = DescriptorSetLayout::new( device, DescriptorSetLayoutCreateInfo { bindings: [( 0, DescriptorSetLayoutBinding { stages: ShaderStages::all_graphics(), ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer) }, )] .into(), ..Default::default() }, ) .unwrap(); assert_eq!( sl.descriptor_counts(), &[(DescriptorType::UniformBuffer, 1)] .into_iter() .collect::>(), ); } }