blob: b69deeed57719c6bfb27d6e6fc671f520782ef6c [file] [log] [blame]
// Copyright (c) 2016 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.
//! 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<DescriptorSetLayout>;
/// Creates a [`DescriptorSetWithOffsets`] with the given dynamic offsets.
fn offsets<I>(self, dynamic_offsets: I) -> DescriptorSetWithOffsets
where
Self: Sized + Send + Sync + 'static,
I: IntoIterator<Item = u32>,
{
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<T> DescriptorSet for T
where
T: SafeDeref,
T::Target: DescriptorSet,
{
#[inline]
fn inner(&self) -> &UnsafeDescriptorSet {
(**self).inner()
}
#[inline]
fn layout(&self) -> &Arc<DescriptorSetLayout> {
(**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<H: Hasher>(&self, state: &mut H) {
self.inner().internal_object().hash(state);
self.device().hash(state);
}
}
pub struct DescriptorSetWithOffsets {
descriptor_set: Box<dyn DescriptorSet + Send + Sync>,
dynamic_offsets: SmallVec<[u32; 4]>,
}
impl DescriptorSetWithOffsets {
#[inline]
pub fn new<S, O>(descriptor_set: S, dynamic_offsets: O) -> Self
where
S: DescriptorSet + Send + Sync + 'static,
O: IntoIterator<Item = u32>,
{
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<dyn DescriptorSet + Send + Sync>,
impl ExactSizeIterator<Item = u32>,
) {
(self.descriptor_set, self.dynamic_offsets.into_iter())
}
}
impl<S> From<S> for DescriptorSetWithOffsets
where
S: DescriptorSet + Send + Sync + 'static,
{
#[inline]
fn from(descriptor_set: S) -> Self {
Self::new(descriptor_set, std::iter::empty())
}
}