// Copyright (c) 2016 The vulkano developers // Licensed under the Apache License, Version 2.0 // or the MIT // license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. //! Bindings between shaders and the resources they access. //! //! # Overview //! //! In order to access a buffer or an image from a shader, that buffer or image must be put in a //! *descriptor*. Each descriptor contains one buffer or one image alongside with the way that it //! can be accessed. A descriptor can also be an array, in which case it contains multiple buffers //! or images that all have the same layout. //! //! Descriptors are grouped in what is called *descriptor sets*. In Vulkan you don't bind //! individual descriptors one by one, but you create then bind descriptor sets one by one. As //! binding a descriptor set has (small but non-null) a cost, you are encouraged to put descriptors //! that are often used together in the same set so that you can keep the same set binding through //! multiple draws. //! //! # Example //! //! > **Note**: This section describes the simple way to bind resources. There are more optimized //! > ways. //! //! There are two steps to give access to a resource in a shader: creating the descriptor set, and //! passing the descriptor sets when drawing. //! //! ## Creating a descriptor set //! //! TODO: write example for: PersistentDescriptorSet::start(layout.clone()).add_buffer(data_buffer.clone()) //! //! ## Passing the descriptor set when drawing //! //! TODO: write //! //! # When drawing //! //! When you call a function that adds a draw command to a command buffer, one of the parameters //! corresponds to the list of descriptor sets to use. Vulkano will check that what you passed is //! compatible with the layout of the pipeline. //! //! TODO: talk about perfs of changing sets //! //! # Descriptor sets creation and management //! //! There are three concepts in Vulkan related to descriptor sets: //! //! - A `DescriptorSetLayout` is a Vulkan object that describes to the Vulkan implementation the //! layout of a future descriptor set. When you allocate a descriptor set, you have to pass an //! instance of this object. This is represented with the `DescriptorSetLayout` type in //! vulkano. //! - A `DescriptorPool` is a Vulkan object that holds the memory of descriptor sets and that can //! be used to allocate and free individual descriptor sets. This is represented with the //! `UnsafeDescriptorPool` type in vulkano. //! - A `DescriptorSet` contains the bindings to resources and is allocated from a pool. This is //! represented with the `UnsafeDescriptorSet` type in vulkano. //! //! In addition to this, vulkano defines the following: //! //! - The `DescriptorPool` trait can be implemented on types from which you can allocate and free //! descriptor sets. However it is different from Vulkan descriptor pools in the sense that an //! implementation of the `DescriptorPool` trait can manage multiple Vulkan descriptor pools. //! - The `StdDescriptorPool` type is a default implementation of the `DescriptorPool` trait. //! - The `DescriptorSet` trait is implemented on types that wrap around Vulkan descriptor sets in //! a safe way. A Vulkan descriptor set is inherently unsafe, so we need safe wrappers around //! them. //! - The `SimpleDescriptorSet` type is a default implementation of the `DescriptorSet` trait. //! - The `DescriptorSetsCollection` trait is implemented on collections of types that implement //! `DescriptorSet`. It is what you pass to the draw functions. pub use self::collection::DescriptorSetsCollection; pub use self::fixed_size_pool::FixedSizeDescriptorSetsPool; use self::layout::DescriptorSetLayout; pub use self::persistent::PersistentDescriptorSet; pub use self::persistent::PersistentDescriptorSetBuildError; pub use self::persistent::PersistentDescriptorSetError; use self::sys::UnsafeDescriptorSet; use crate::buffer::BufferAccess; use crate::descriptor_set::layout::{DescriptorBufferDesc, DescriptorDescTy}; use crate::device::DeviceOwned; use crate::image::view::ImageViewAbstract; use crate::SafeDeref; use crate::VulkanObject; use smallvec::SmallVec; use std::hash::Hash; use std::hash::Hasher; use std::sync::Arc; mod collection; pub mod fixed_size_pool; pub mod layout; pub mod persistent; pub mod pool; pub mod sys; /// Trait for objects that contain a collection of resources that will be accessible by shaders. /// /// Objects of this type can be passed when submitting a draw command. pub unsafe trait DescriptorSet: DeviceOwned { /// Returns the inner `UnsafeDescriptorSet`. fn inner(&self) -> &UnsafeDescriptorSet; /// Returns the layout of this descriptor set. fn layout(&self) -> &Arc; /// Creates a [`DescriptorSetWithOffsets`] with the given dynamic offsets. fn offsets(self, dynamic_offsets: I) -> DescriptorSetWithOffsets where Self: Sized + Send + Sync + 'static, I: IntoIterator, { DescriptorSetWithOffsets::new(self, dynamic_offsets) } /// Returns the number of buffers within this descriptor set. fn num_buffers(&self) -> usize; /// Returns the `index`th buffer of this descriptor set, or `None` if out of range. Also /// returns the index of the descriptor that uses this buffer. /// /// The valid range is between 0 and `num_buffers()`. fn buffer(&self, index: usize) -> Option<(&dyn BufferAccess, u32)>; /// Returns the number of images within this descriptor set. fn num_images(&self) -> usize; /// Returns the `index`th image of this descriptor set, or `None` if out of range. Also returns /// the index of the descriptor that uses this image. /// /// The valid range is between 0 and `num_images()`. fn image(&self, index: usize) -> Option<(&dyn ImageViewAbstract, u32)>; } unsafe impl DescriptorSet for T where T: SafeDeref, T::Target: DescriptorSet, { #[inline] fn inner(&self) -> &UnsafeDescriptorSet { (**self).inner() } #[inline] fn layout(&self) -> &Arc { (**self).layout() } #[inline] fn num_buffers(&self) -> usize { (**self).num_buffers() } #[inline] fn buffer(&self, index: usize) -> Option<(&dyn BufferAccess, u32)> { (**self).buffer(index) } #[inline] fn num_images(&self) -> usize { (**self).num_images() } #[inline] fn image(&self, index: usize) -> Option<(&dyn ImageViewAbstract, u32)> { (**self).image(index) } } impl PartialEq for dyn DescriptorSet + Send + Sync { #[inline] fn eq(&self, other: &Self) -> bool { self.inner().internal_object() == other.inner().internal_object() && self.device() == other.device() } } impl Eq for dyn DescriptorSet + Send + Sync {} impl Hash for dyn DescriptorSet + Send + Sync { #[inline] fn hash(&self, state: &mut H) { self.inner().internal_object().hash(state); self.device().hash(state); } } pub struct DescriptorSetWithOffsets { descriptor_set: Box, dynamic_offsets: SmallVec<[u32; 4]>, } impl DescriptorSetWithOffsets { #[inline] pub fn new(descriptor_set: S, dynamic_offsets: O) -> Self where S: DescriptorSet + Send + Sync + 'static, O: IntoIterator, { let dynamic_offsets: SmallVec<_> = dynamic_offsets.into_iter().collect(); let layout = descriptor_set.layout(); let properties = layout.device().physical_device().properties(); let min_uniform_off_align = properties.min_uniform_buffer_offset_alignment as u32; let min_storage_off_align = properties.min_storage_buffer_offset_alignment as u32; let mut dynamic_offset_index = 0; // Ensure that the number of dynamic_offsets is correct and that each // dynamic offset is a multiple of the minimum offset alignment specified // by the physical device. for desc in layout.desc().bindings() { let desc = desc.as_ref().unwrap(); if let DescriptorDescTy::Buffer(DescriptorBufferDesc { dynamic: Some(true), storage, }) = desc.ty { // Don't check alignment if there are not enough offsets anyway if dynamic_offsets.len() > dynamic_offset_index { if storage { assert!( dynamic_offsets[dynamic_offset_index] % min_storage_off_align == 0, "Dynamic storage buffer offset must be a multiple of min_storage_buffer_offset_alignment: got {}, expected a multiple of {}", dynamic_offsets[dynamic_offset_index], min_storage_off_align ); } else { assert!( dynamic_offsets[dynamic_offset_index] % min_uniform_off_align == 0, "Dynamic uniform buffer offset must be a multiple of min_uniform_buffer_offset_alignment: got {}, expected a multiple of {}", dynamic_offsets[dynamic_offset_index], min_uniform_off_align ); } } dynamic_offset_index += 1; } } assert!( !(dynamic_offsets.len() < dynamic_offset_index), "Too few dynamic offsets: got {}, expected {}", dynamic_offsets.len(), dynamic_offset_index ); assert!( !(dynamic_offsets.len() > dynamic_offset_index), "Too many dynamic offsets: got {}, expected {}", dynamic_offsets.len(), dynamic_offset_index ); DescriptorSetWithOffsets { descriptor_set: Box::new(descriptor_set), dynamic_offsets, } } #[inline] pub fn as_ref(&self) -> (&dyn DescriptorSet, &[u32]) { (&self.descriptor_set, &self.dynamic_offsets) } #[inline] pub fn into_tuple( self, ) -> ( Box, impl ExactSizeIterator, ) { (self.descriptor_set, self.dynamic_offsets.into_iter()) } } impl From for DescriptorSetWithOffsets where S: DescriptorSet + Send + Sync + 'static, { #[inline] fn from(descriptor_set: S) -> Self { Self::new(descriptor_set, std::iter::empty()) } }