blob: 248f794cbb451db11147eb2178d810de04c46a04 [file] [log] [blame]
// 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)?,
})
}
}