diff options
Diffstat (limited to 'src/descriptor_set/layout.rs')
-rw-r--r-- | src/descriptor_set/layout.rs | 951 |
1 files changed, 951 insertions, 0 deletions
diff --git a/src/descriptor_set/layout.rs b/src/descriptor_set/layout.rs new file mode 100644 index 0000000..7000e0a --- /dev/null +++ b/src/descriptor_set/layout.rs @@ -0,0 +1,951 @@ +// Copyright (c) 2021 The vulkano developers +// Licensed under the Apache License, Version 2.0 +// <LICENSE-APACHE or +// https://www.apache.org/licenses/LICENSE-2.0> or the MIT +// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, +// 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<Device>, + id: NonZeroU64, + + bindings: BTreeMap<u32, DescriptorSetLayoutBinding>, + push_descriptor: bool, + + descriptor_counts: HashMap<DescriptorType, u32>, +} + +impl DescriptorSetLayout { + /// Creates a new `DescriptorSetLayout`. + #[inline] + pub fn new( + device: Arc<Device>, + mut create_info: DescriptorSetLayoutCreateInfo, + ) -> Result<Arc<DescriptorSetLayout>, 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<Device>, + handle: ash::vk::DescriptorSetLayout, + create_info: DescriptorSetLayoutCreateInfo, + ) -> Arc<DescriptorSetLayout> { + 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<HashMap<DescriptorType, u32>, 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::<u32>() + > 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<ash::vk::DescriptorSetLayout, DescriptorSetLayoutCreationError> { + 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<Box<[ash::vk::Sampler]>> = 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::<Vec<_>>() + .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<u32, DescriptorSetLayoutBinding> { + &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<DescriptorType, u32> { + &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<Device> { + &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<VulkanError> 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<RequirementNotMet> 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<u32, DescriptorSetLayoutBinding>, + + /// 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<Self> { + let mut create_infos: Vec<Self> = 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<Arc<Sampler>>, + + 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<DescriptorType>, + 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::<HashMap<_, _>>(), + ); + } +} |