aboutsummaryrefslogtreecommitdiff
path: root/src/descriptor_set/layout.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/descriptor_set/layout.rs')
-rw-r--r--src/descriptor_set/layout.rs951
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<_, _>>(),
+ );
+ }
+}