//! Descriptor sets and layouts. //! //! A [`Descriptor`] is an object that describes the connection between a resource, such as //! an `Image` or `Buffer`, and a variable in a shader. Descriptors are organized into //! `DescriptorSet`s, each of which contains multiple descriptors that are bound and unbound to //! shaders as a single unit. The contents of each descriptor in a set is defined by a //! `DescriptorSetLayout` which is in turn built of [`DescriptorSetLayoutBinding`]s. A `DescriptorSet` //! is then allocated from a [`DescriptorPool`] using the `DescriptorSetLayout`, and specific [`Descriptor`]s are //! then bound to each binding point in the set using a [`DescriptorSetWrite`] and/or [`DescriptorSetCopy`]. //! Each descriptor set may contain descriptors to multiple different sorts of resources, and a shader may //! use multiple descriptor sets at a time. //! //! [`Descriptor`]: enum.Descriptor.html //! [`DescriptorSetLayoutBinding`]: struct.DescriptorSetLayoutBinding.html //! [`DescriptorPool`]: trait.DescriptorPool.html //! [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html //! [`DescriptorSetCopy`]: struct.DescriptorSetWrite.html use std::{borrow::Borrow, fmt, iter}; use crate::{ buffer::SubRange, device::OutOfMemory, image::Layout, pso::ShaderStageFlags, Backend, PseudoVec, }; /// pub type DescriptorSetIndex = u16; /// pub type DescriptorBinding = u32; /// pub type DescriptorArrayIndex = usize; /// Specific type of a buffer. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum BufferDescriptorType { /// Storage buffers allow load, store, and atomic operations. Storage { /// If true, store operations are not permitted on this buffer. read_only: bool, }, /// Uniform buffers provide constant data to be accessed in a shader. Uniform, } /// Format of a buffer. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum BufferDescriptorFormat { /// The buffer is interpreted as a structure defined in a shader. Structured { /// If true, the buffer is accessed by an additional offset specified in /// the `offsets` parameter of `CommandBuffer::bind_*_descriptor_sets`. dynamic_offset: bool, }, /// The buffer is interpreted as a 1-D array of texels, which undergo format /// conversion when loaded in a shader. Texel, } /// Specific type of an image descriptor. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum ImageDescriptorType { /// A sampled image allows sampling operations. Sampled { /// If true, this descriptor corresponds to both a sampled image and a /// sampler to be used with that image. with_sampler: bool, }, /// A storage image allows load, store and atomic operations. Storage { /// If true, store operations are not permitted on this image. read_only: bool, }, } /// The type of a descriptor. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum DescriptorType { /// A descriptor associated with sampler. Sampler, /// A descriptor associated with an image. Image { /// The specific type of this image descriptor. ty: ImageDescriptorType, }, /// A descriptor associated with a buffer. Buffer { /// The type of this buffer descriptor. ty: BufferDescriptorType, /// The format of this buffer descriptor. format: BufferDescriptorFormat, }, /// A descriptor associated with an input attachment. InputAttachment, } /// Information about the contents of and in which stages descriptors may be bound to a descriptor /// set at a certain binding point. Multiple `DescriptorSetLayoutBinding`s are assembled into /// a `DescriptorSetLayout`, which is then allocated into a `DescriptorSet` using a /// [`DescriptorPool`]. /// /// A descriptor set consists of multiple binding points. /// Each binding point contains one or multiple descriptors of a certain type. /// The binding point is only valid for the pipelines stages specified. /// /// The binding _must_ match with the corresponding shader interface. /// /// [`DescriptorPool`]: trait.DescriptorPool.html #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DescriptorSetLayoutBinding { /// Descriptor bindings range. pub binding: DescriptorBinding, /// Type of the bound descriptors. pub ty: DescriptorType, /// Number of descriptors in the array. /// /// *Note*: If count is zero, the binding point is reserved /// and can't be accessed from any shader stages. pub count: DescriptorArrayIndex, /// Valid shader stages. pub stage_flags: ShaderStageFlags, /// Use the associated list of immutable samplers. pub immutable_samplers: bool, } /// Set of descriptors of a specific type. #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct DescriptorRangeDesc { /// Type of the stored descriptors. pub ty: DescriptorType, /// Amount of space. pub count: usize, } /// An error allocating descriptor sets from a pool. #[derive(Clone, Debug, PartialEq)] pub enum AllocationError { /// OutOfMemory::Host: Memory allocation on the host side failed. /// OutOfMemory::Device: Memory allocation on the device side failed. /// This could be caused by a lack of memory or pool fragmentation. OutOfMemory(OutOfMemory), /// Memory allocation failed as there is not enough in the pool. /// This could be caused by too many descriptor sets being created. OutOfPoolMemory, /// Memory allocation failed due to pool fragmentation. FragmentedPool, /// Descriptor set allocation failed as the layout is incompatible with the pool. IncompatibleLayout, } impl std::fmt::Display for AllocationError { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { AllocationError::OutOfMemory(OutOfMemory::Host) => { write!(fmt, "Failed to allocate descriptor set: Out of host memory") } AllocationError::OutOfMemory(OutOfMemory::Device) => write!( fmt, "Failed to allocate descriptor set: Out of device memory" ), AllocationError::OutOfPoolMemory => { write!(fmt, "Failed to allocate descriptor set: Out of pool memory") } AllocationError::FragmentedPool => { write!(fmt, "Failed to allocate descriptor set: Pool is fragmented") } AllocationError::IncompatibleLayout => write!( fmt, "Failed to allocate descriptor set: Incompatible layout" ), } } } impl std::error::Error for AllocationError {} /// A descriptor pool is a collection of memory from which descriptor sets are allocated. pub trait DescriptorPool: Send + Sync + fmt::Debug { /// Allocate a descriptor set from the pool. /// /// The descriptor set will be allocated from the pool according to the corresponding set layout. However, /// specific descriptors must still be written to the set before use using a [`DescriptorSetWrite`] or /// [`DescriptorSetCopy`]. /// /// Descriptors will become invalid once the pool is reset. Usage of invalidated descriptor sets results /// in undefined behavior. /// /// [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html /// [`DescriptorSetCopy`]: struct.DescriptorSetCopy.html unsafe fn allocate_set( &mut self, layout: &B::DescriptorSetLayout, ) -> Result { let mut result = PseudoVec(None); self.allocate(iter::once(layout), &mut result)?; Ok(result.0.unwrap()) } /// Allocate multiple descriptor sets from the pool. /// /// The descriptor set will be allocated from the pool according to the corresponding set layout. However, /// specific descriptors must still be written to the set before use using a [`DescriptorSetWrite`] or /// [`DescriptorSetCopy`]. /// /// Each descriptor set will be allocated from the pool according to the corresponding set layout. /// Descriptors will become invalid once the pool is reset. Usage of invalidated descriptor sets results /// in undefined behavior. /// /// [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html /// [`DescriptorSetCopy`]: struct.DescriptorSetCopy.html unsafe fn allocate(&mut self, layouts: I, list: &mut E) -> Result<(), AllocationError> where I: IntoIterator, I::Item: Borrow, E: Extend, { for layout in layouts { let set = self.allocate_set(layout.borrow())?; list.extend(iter::once(set)); } Ok(()) } /// Free the given descriptor sets provided as an iterator. unsafe fn free(&mut self, descriptor_sets: I) where I: IntoIterator; /// Resets a descriptor pool, releasing all resources from all the descriptor sets /// allocated from it and freeing the descriptor sets. Invalidates all descriptor /// sets allocated from the pool; trying to use one after the pool has been reset /// is undefined behavior. unsafe fn reset(&mut self); } /// Writes the actual descriptors to be bound into a descriptor set. /// /// Should be provided to the `write_descriptor_sets` method of a `Device`. #[derive(Debug)] pub struct DescriptorSetWrite<'a, B: Backend, WI> where WI: IntoIterator, WI::Item: Borrow>, { /// The descriptor set to modify. pub set: &'a B::DescriptorSet, /// Binding index to start writing at. /// /// *Note*: when there are more descriptors provided than /// array elements left in the specified binding starting /// at the specified offset, the updates are spilled onto /// the next binding (starting with offset 0), and so on. pub binding: DescriptorBinding, /// Offset into the array to copy to. pub array_offset: DescriptorArrayIndex, /// Descriptors to write to the set. pub descriptors: WI, } /// A handle to a specific shader resource that can be bound for use in a `DescriptorSet`. /// Usually provided in a [`DescriptorSetWrite`] /// /// [`DescriptorSetWrite`]: struct.DescriptorSetWrite.html #[allow(missing_docs)] #[derive(Clone, Debug)] pub enum Descriptor<'a, B: Backend> { Sampler(&'a B::Sampler), Image(&'a B::ImageView, Layout), CombinedImageSampler(&'a B::ImageView, Layout, &'a B::Sampler), Buffer(&'a B::Buffer, SubRange), TexelBuffer(&'a B::BufferView), } /// Copies a range of descriptors to be bound from one descriptor set to another. /// /// Should be provided to the `copy_descriptor_sets` method of a `Device`. #[derive(Clone, Copy, Debug)] pub struct DescriptorSetCopy<'a, B: Backend> { /// Descriptor set to copy from. pub src_set: &'a B::DescriptorSet, /// Binding to copy from. /// /// *Note*: when there are more descriptors required than /// array elements left in the specified binding starting /// at the specified offset, the updates are taken from /// the next binding (starting with offset 0), and so on. pub src_binding: DescriptorBinding, /// Offset into the descriptor array to start copying from. pub src_array_offset: DescriptorArrayIndex, /// Descriptor set to copy to. pub dst_set: &'a B::DescriptorSet, /// Binding to copy to. /// /// *Note*: when there are more descriptors provided than /// array elements left in the specified binding starting /// at the specified offset, the updates are spilled onto /// the next binding (starting with offset 0), and so on. pub dst_binding: DescriptorBinding, /// Offset into the descriptor array to copy to. pub dst_array_offset: DescriptorArrayIndex, /// How many descriptors to copy. pub count: usize, } bitflags! { /// Descriptor pool creation flags. pub struct DescriptorPoolCreateFlags: u32 { /// Specifies that descriptor sets are allowed to be freed from the pool /// individually. const FREE_DESCRIPTOR_SET = 0x1; } }