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