diff options
Diffstat (limited to 'src/descriptor_set/fixed_size_pool.rs')
-rw-r--r-- | src/descriptor_set/fixed_size_pool.rs | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/src/descriptor_set/fixed_size_pool.rs b/src/descriptor_set/fixed_size_pool.rs new file mode 100644 index 0000000..248f794 --- /dev/null +++ b/src/descriptor_set/fixed_size_pool.rs @@ -0,0 +1,604 @@ +// Copyright (c) 2017 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. + +//! Pool of descriptor sets of a specific capacity that are automatically reclaimed. +//! +//! You are encouraged to use this type when you need a different descriptor set at each frame, or +//! regularly during the execution. +//! +//! # Example +//! +//! At initialization, create a `FixedSizeDescriptorSetsPool`. +//! +//! ```rust +//! use vulkano::descriptor_set::FixedSizeDescriptorSetsPool; +//! # use vulkano::pipeline::GraphicsPipelineAbstract; +//! # use std::sync::Arc; +//! # let graphics_pipeline: Arc<GraphicsPipelineAbstract> = return; +//! // use vulkano::pipeline::GraphicsPipelineAbstract; +//! // let graphics_pipeline: Arc<GraphicsPipelineAbstract> = ...; +//! +//! let layout = graphics_pipeline.layout().descriptor_set_layouts().get(0).unwrap(); +//! let pool = FixedSizeDescriptorSetsPool::new(layout.clone()); +//! ``` +//! +//! You would then typically store the pool in a struct for later. Then whenever you need a +//! descriptor set, call `pool.next()` to start the process of building it. +//! +//! ```rust +//! # use std::sync::Arc; +//! # use vulkano::descriptor_set::FixedSizeDescriptorSetsPool; +//! # use vulkano::pipeline::GraphicsPipelineAbstract; +//! # let mut pool: FixedSizeDescriptorSetsPool = return; +//! let descriptor_set = pool.next() +//! //.add_buffer(...) +//! //.add_sampled_image(...) +//! .build().unwrap(); +//! ``` +//! +//! Note that `next()` requires exclusive (`mut`) access to the pool. You can use a `Mutex` around +//! the pool if you can't provide this. + +use crate::buffer::BufferAccess; +use crate::buffer::BufferViewRef; +use crate::descriptor_set::layout::DescriptorSetLayout; +use crate::descriptor_set::persistent::*; +use crate::descriptor_set::pool::DescriptorPool; +use crate::descriptor_set::pool::DescriptorPoolAlloc; +use crate::descriptor_set::pool::DescriptorPoolAllocError; +use crate::descriptor_set::pool::UnsafeDescriptorPool; +use crate::descriptor_set::DescriptorSet; +use crate::descriptor_set::UnsafeDescriptorSet; +use crate::device::Device; +use crate::device::DeviceOwned; +use crate::image::view::ImageViewAbstract; +use crate::sampler::Sampler; +use crate::OomError; +use crate::VulkanObject; +use crossbeam_queue::SegQueue; +use std::hash::Hash; +use std::hash::Hasher; +use std::sync::Arc; + +/// Pool of descriptor sets of a specific capacity that are automatically reclaimed. +#[derive(Clone)] +pub struct FixedSizeDescriptorSetsPool { + layout: Arc<DescriptorSetLayout>, + // We hold a local implementation of the `DescriptorPool` trait for our own purpose. Since we + // don't want to expose this trait impl in our API, we use a separate struct. + pool: LocalPool, +} + +impl FixedSizeDescriptorSetsPool { + /// Initializes a new pool. The pool is configured to allocate sets that corresponds to the + /// parameters passed to this function. + pub fn new(layout: Arc<DescriptorSetLayout>) -> FixedSizeDescriptorSetsPool { + let device = layout.device().clone(); + + FixedSizeDescriptorSetsPool { + layout, + pool: LocalPool { + device, + next_capacity: 3, + current_pool: None, + }, + } + } + + /// Starts the process of building a new descriptor set. + /// + /// The set will corresponds to the set layout that was passed to `new`. + #[inline] + pub fn next(&mut self) -> FixedSizeDescriptorSetBuilder<()> { + let inner = PersistentDescriptorSet::start(self.layout.clone()); + + FixedSizeDescriptorSetBuilder { pool: self, inner } + } +} + +/// A descriptor set created from a `FixedSizeDescriptorSetsPool`. +pub struct FixedSizeDescriptorSet<R> { + inner: PersistentDescriptorSet<R, LocalPoolAlloc>, +} + +unsafe impl<R> DescriptorSet for FixedSizeDescriptorSet<R> +where + R: PersistentDescriptorSetResources, +{ + #[inline] + fn inner(&self) -> &UnsafeDescriptorSet { + self.inner.inner() + } + + #[inline] + fn layout(&self) -> &Arc<DescriptorSetLayout> { + self.inner.layout() + } + + #[inline] + fn num_buffers(&self) -> usize { + self.inner.num_buffers() + } + + #[inline] + fn buffer(&self, index: usize) -> Option<(&dyn BufferAccess, u32)> { + self.inner.buffer(index) + } + + #[inline] + fn num_images(&self) -> usize { + self.inner.num_images() + } + + #[inline] + fn image(&self, index: usize) -> Option<(&dyn ImageViewAbstract, u32)> { + self.inner.image(index) + } +} + +unsafe impl<R> DeviceOwned for FixedSizeDescriptorSet<R> { + #[inline] + fn device(&self) -> &Arc<Device> { + self.inner.device() + } +} + +impl<R> PartialEq for FixedSizeDescriptorSet<R> +where + R: PersistentDescriptorSetResources, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.inner().internal_object() == other.inner().internal_object() + && self.device() == other.device() + } +} + +impl<R> Eq for FixedSizeDescriptorSet<R> where R: PersistentDescriptorSetResources {} + +impl<R> Hash for FixedSizeDescriptorSet<R> +where + R: PersistentDescriptorSetResources, +{ + #[inline] + fn hash<H: Hasher>(&self, state: &mut H) { + self.inner().internal_object().hash(state); + self.device().hash(state); + } +} + +// The fields of this struct can be considered as fields of the `FixedSizeDescriptorSet`. They are +// in a separate struct because we don't want to expose the fact that we implement the +// `DescriptorPool` trait. +#[derive(Clone)] +struct LocalPool { + // The `LocalPoolInner` struct contains an actual Vulkan pool. Every time it is full, we create + // a new pool and replace the current one with the new one. + current_pool: Option<Arc<LocalPoolInner>>, + // Capacity to use when we create a new Vulkan pool. + next_capacity: u32, + // The Vulkan device. + device: Arc<Device>, +} + +struct LocalPoolInner { + // The actual Vulkan descriptor pool. This field isn't actually used anywhere, but we need to + // keep the pool alive in order to keep the descriptor sets valid. + actual_pool: UnsafeDescriptorPool, + + // List of descriptor sets. When `alloc` is called, a descriptor will be extracted from this + // list. When a `LocalPoolAlloc` is dropped, its descriptor set is put back in this list. + reserve: SegQueue<UnsafeDescriptorSet>, +} + +struct LocalPoolAlloc { + // The `LocalPoolInner` we were allocated from. We need to keep a copy of it in each allocation + // so that we can put back the allocation in the list in our `Drop` impl. + pool: Arc<LocalPoolInner>, + + // The actual descriptor set, wrapped inside an `Option` so that we can extract it in our + // `Drop` impl. + actual_alloc: Option<UnsafeDescriptorSet>, +} + +unsafe impl DescriptorPool for LocalPool { + type Alloc = LocalPoolAlloc; + + fn alloc(&mut self, layout: &DescriptorSetLayout) -> Result<Self::Alloc, OomError> { + loop { + // Try to extract a descriptor from the current pool if any exist. + // This is the most common case. + if let Some(ref mut current_pool) = self.current_pool { + if let Some(already_existing_set) = current_pool.reserve.pop() { + return Ok(LocalPoolAlloc { + actual_alloc: Some(already_existing_set), + pool: current_pool.clone(), + }); + } + } + + // If we failed to grab an existing set, that means the current pool is full. Create a + // new one of larger capacity. + let count = *layout.descriptors_count() * self.next_capacity; + let mut new_pool = + UnsafeDescriptorPool::new(self.device.clone(), &count, self.next_capacity, false)?; + let alloc = unsafe { + match new_pool.alloc((0..self.next_capacity).map(|_| layout)) { + Ok(iter) => { + let stack = SegQueue::new(); + for elem in iter { + stack.push(elem); + } + stack + } + Err(DescriptorPoolAllocError::OutOfHostMemory) => { + return Err(OomError::OutOfHostMemory); + } + Err(DescriptorPoolAllocError::OutOfDeviceMemory) => { + return Err(OomError::OutOfDeviceMemory); + } + Err(DescriptorPoolAllocError::FragmentedPool) => { + // This can't happen as we don't free individual sets. + unreachable!() + } + Err(DescriptorPoolAllocError::OutOfPoolMemory) => unreachable!(), + } + }; + + self.next_capacity = self.next_capacity.saturating_mul(2); + self.current_pool = Some(Arc::new(LocalPoolInner { + actual_pool: new_pool, + reserve: alloc, + })); + } + } +} + +unsafe impl DeviceOwned for LocalPool { + #[inline] + fn device(&self) -> &Arc<Device> { + &self.device + } +} + +impl DescriptorPoolAlloc for LocalPoolAlloc { + #[inline] + fn inner(&self) -> &UnsafeDescriptorSet { + self.actual_alloc.as_ref().unwrap() + } + + #[inline] + fn inner_mut(&mut self) -> &mut UnsafeDescriptorSet { + self.actual_alloc.as_mut().unwrap() + } +} + +impl Drop for LocalPoolAlloc { + fn drop(&mut self) { + let inner = self.actual_alloc.take().unwrap(); + self.pool.reserve.push(inner); + } +} + +/// Prototype of a `FixedSizeDescriptorSet`. +/// +/// The template parameter `R` is an unspecified type that represents the list of resources. +/// +/// See the docs of `FixedSizeDescriptorSetsPool` for an example. +pub struct FixedSizeDescriptorSetBuilder<'a, R> { + pool: &'a mut FixedSizeDescriptorSetsPool, + inner: PersistentDescriptorSetBuilder<R>, +} + +impl<'a, R> FixedSizeDescriptorSetBuilder<'a, R> { + /// Builds a `FixedSizeDescriptorSet` from the builder. + #[inline] + pub fn build(self) -> Result<FixedSizeDescriptorSet<R>, PersistentDescriptorSetBuildError> { + let inner = self.inner.build_with_pool(&mut self.pool.pool)?; + Ok(FixedSizeDescriptorSet { inner }) + } + + /// Call this function if the next element of the set is an array in order to set the value of + /// each element. + /// + /// Returns an error if the descriptor is empty. + /// + /// This function can be called even if the descriptor isn't an array, and it is valid to enter + /// the "array", add one element, then leave. + #[inline] + pub fn enter_array( + self, + ) -> Result<FixedSizeDescriptorSetBuilderArray<'a, R>, PersistentDescriptorSetError> { + Ok(FixedSizeDescriptorSetBuilderArray { + pool: self.pool, + inner: self.inner.enter_array()?, + }) + } + + /// Skips the current descriptor if it is empty. + #[inline] + pub fn add_empty( + self, + ) -> Result<FixedSizeDescriptorSetBuilder<'a, R>, PersistentDescriptorSetError> { + Ok(FixedSizeDescriptorSetBuilder { + pool: self.pool, + inner: self.inner.add_empty()?, + }) + } + + /// Binds a buffer as the next descriptor. + /// + /// An error is returned if the buffer isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the buffer doesn't have the same device as the descriptor set layout. + /// + #[inline] + pub fn add_buffer<T>( + self, + buffer: T, + ) -> Result< + FixedSizeDescriptorSetBuilder<'a, (R, PersistentDescriptorSetBuf<T>)>, + PersistentDescriptorSetError, + > + where + T: BufferAccess, + { + Ok(FixedSizeDescriptorSetBuilder { + pool: self.pool, + inner: self.inner.add_buffer(buffer)?, + }) + } + + /// Binds a buffer view as the next descriptor. + /// + /// An error is returned if the buffer isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the buffer view doesn't have the same device as the descriptor set layout. + /// + pub fn add_buffer_view<T>( + self, + view: T, + ) -> Result< + FixedSizeDescriptorSetBuilder<'a, (R, PersistentDescriptorSetBufView<T>)>, + PersistentDescriptorSetError, + > + where + T: BufferViewRef, + { + Ok(FixedSizeDescriptorSetBuilder { + pool: self.pool, + inner: self.inner.add_buffer_view(view)?, + }) + } + + /// Binds an image view as the next descriptor. + /// + /// An error is returned if the image view isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the image view doesn't have the same device as the descriptor set layout. + /// + #[inline] + pub fn add_image<T>( + self, + image_view: T, + ) -> Result< + FixedSizeDescriptorSetBuilder<'a, (R, PersistentDescriptorSetImg<T>)>, + PersistentDescriptorSetError, + > + where + T: ImageViewAbstract, + { + Ok(FixedSizeDescriptorSetBuilder { + pool: self.pool, + inner: self.inner.add_image(image_view)?, + }) + } + + /// Binds an image view with a sampler as the next descriptor. + /// + /// An error is returned if the image view isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the image view or the sampler doesn't have the same device as the descriptor set layout. + /// + #[inline] + pub fn add_sampled_image<T>( + self, + image_view: T, + sampler: Arc<Sampler>, + ) -> Result< + FixedSizeDescriptorSetBuilder< + 'a, + ( + (R, PersistentDescriptorSetImg<T>), + PersistentDescriptorSetSampler, + ), + >, + PersistentDescriptorSetError, + > + where + T: ImageViewAbstract, + { + Ok(FixedSizeDescriptorSetBuilder { + pool: self.pool, + inner: self.inner.add_sampled_image(image_view, sampler)?, + }) + } + + /// Binds a sampler as the next descriptor. + /// + /// An error is returned if the sampler isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the sampler doesn't have the same device as the descriptor set layout. + /// + #[inline] + pub fn add_sampler( + self, + sampler: Arc<Sampler>, + ) -> Result< + FixedSizeDescriptorSetBuilder<'a, (R, PersistentDescriptorSetSampler)>, + PersistentDescriptorSetError, + > { + Ok(FixedSizeDescriptorSetBuilder { + pool: self.pool, + inner: self.inner.add_sampler(sampler)?, + }) + } +} + +/// Same as `FixedSizeDescriptorSetBuilder`, but we're in an array. +pub struct FixedSizeDescriptorSetBuilderArray<'a, R> { + pool: &'a mut FixedSizeDescriptorSetsPool, + inner: PersistentDescriptorSetBuilderArray<R>, +} + +impl<'a, R> FixedSizeDescriptorSetBuilderArray<'a, R> { + /// Leaves the array. Call this once you added all the elements of the array. + pub fn leave_array( + self, + ) -> Result<FixedSizeDescriptorSetBuilder<'a, R>, PersistentDescriptorSetError> { + Ok(FixedSizeDescriptorSetBuilder { + pool: self.pool, + inner: self.inner.leave_array()?, + }) + } + + /// Binds a buffer as the next element in the array. + /// + /// An error is returned if the buffer isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the buffer doesn't have the same device as the descriptor set layout. + /// + pub fn add_buffer<T>( + self, + buffer: T, + ) -> Result< + FixedSizeDescriptorSetBuilderArray<'a, (R, PersistentDescriptorSetBuf<T>)>, + PersistentDescriptorSetError, + > + where + T: BufferAccess, + { + Ok(FixedSizeDescriptorSetBuilderArray { + pool: self.pool, + inner: self.inner.add_buffer(buffer)?, + }) + } + + /// Binds a buffer view as the next element in the array. + /// + /// An error is returned if the buffer isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the buffer view doesn't have the same device as the descriptor set layout. + /// + pub fn add_buffer_view<T>( + self, + view: T, + ) -> Result< + FixedSizeDescriptorSetBuilderArray<'a, (R, PersistentDescriptorSetBufView<T>)>, + PersistentDescriptorSetError, + > + where + T: BufferViewRef, + { + Ok(FixedSizeDescriptorSetBuilderArray { + pool: self.pool, + inner: self.inner.add_buffer_view(view)?, + }) + } + + /// Binds an image view as the next element in the array. + /// + /// An error is returned if the image view isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the image view doesn't have the same device as the descriptor set layout. + /// + pub fn add_image<T>( + self, + image_view: T, + ) -> Result< + FixedSizeDescriptorSetBuilderArray<'a, (R, PersistentDescriptorSetImg<T>)>, + PersistentDescriptorSetError, + > + where + T: ImageViewAbstract, + { + Ok(FixedSizeDescriptorSetBuilderArray { + pool: self.pool, + inner: self.inner.add_image(image_view)?, + }) + } + + /// Binds an image view with a sampler as the next element in the array. + /// + /// An error is returned if the image view isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the image or the sampler doesn't have the same device as the descriptor set layout. + /// + pub fn add_sampled_image<T>( + self, + image_view: T, + sampler: Arc<Sampler>, + ) -> Result< + FixedSizeDescriptorSetBuilderArray< + 'a, + ( + (R, PersistentDescriptorSetImg<T>), + PersistentDescriptorSetSampler, + ), + >, + PersistentDescriptorSetError, + > + where + T: ImageViewAbstract, + { + Ok(FixedSizeDescriptorSetBuilderArray { + pool: self.pool, + inner: self.inner.add_sampled_image(image_view, sampler)?, + }) + } + + /// Binds a sampler as the next element in the array. + /// + /// An error is returned if the sampler isn't compatible with the descriptor. + /// + /// # Panic + /// + /// Panics if the sampler doesn't have the same device as the descriptor set layout. + /// + pub fn add_sampler( + self, + sampler: Arc<Sampler>, + ) -> Result< + FixedSizeDescriptorSetBuilderArray<'a, (R, PersistentDescriptorSetSampler)>, + PersistentDescriptorSetError, + > { + Ok(FixedSizeDescriptorSetBuilderArray { + pool: self.pool, + inner: self.inner.add_sampler(sampler)?, + }) + } +} |