diff options
Diffstat (limited to 'src/descriptor_set/pool/sys.rs')
-rw-r--r-- | src/descriptor_set/pool/sys.rs | 503 |
1 files changed, 503 insertions, 0 deletions
diff --git a/src/descriptor_set/pool/sys.rs b/src/descriptor_set/pool/sys.rs new file mode 100644 index 0000000..134ddc7 --- /dev/null +++ b/src/descriptor_set/pool/sys.rs @@ -0,0 +1,503 @@ +// Copyright (c) 2021 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. + +use crate::check_errors; +use crate::descriptor_set::layout::DescriptorSetLayout; +use crate::descriptor_set::pool::DescriptorsCount; +use crate::descriptor_set::UnsafeDescriptorSet; +use crate::device::Device; +use crate::device::DeviceOwned; +use crate::OomError; +use crate::VulkanObject; +use smallvec::SmallVec; +use std::error; +use std::fmt; +use std::mem::MaybeUninit; +use std::ptr; +use std::sync::Arc; +use std::vec::IntoIter as VecIntoIter; + +/// Pool from which descriptor sets are allocated from. +/// +/// A pool has a maximum number of descriptor sets and a maximum number of descriptors (one value +/// per descriptor type) it can allocate. +pub struct UnsafeDescriptorPool { + pool: ash::vk::DescriptorPool, + device: Arc<Device>, +} + +impl UnsafeDescriptorPool { + /// Initializes a new pool. + /// + /// Initializes a pool whose capacity is given by `count` and `max_sets`. At most `count` + /// descriptors or `max_sets` descriptor sets can be allocated at once with this pool. + /// + /// If `free_descriptor_set_bit` is `true`, then individual descriptor sets can be free'd from + /// the pool. Otherwise you must reset or destroy the whole pool at once. + /// + /// # Panic + /// + /// - Panics if all the descriptors count are 0. + /// - Panics if `max_sets` is 0. + /// + pub fn new( + device: Arc<Device>, + count: &DescriptorsCount, + max_sets: u32, + free_descriptor_set_bit: bool, + ) -> Result<UnsafeDescriptorPool, OomError> { + let fns = device.fns(); + + assert_ne!(max_sets, 0, "The maximum number of sets can't be 0"); + + let mut pool_sizes: SmallVec<[_; 10]> = SmallVec::new(); + + macro_rules! elem { + ($field:ident, $ty:expr) => { + if count.$field >= 1 { + pool_sizes.push(ash::vk::DescriptorPoolSize { + ty: $ty, + descriptor_count: count.$field, + }); + } + }; + } + + elem!(uniform_buffer, ash::vk::DescriptorType::UNIFORM_BUFFER); + elem!(storage_buffer, ash::vk::DescriptorType::STORAGE_BUFFER); + elem!( + uniform_buffer_dynamic, + ash::vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC + ); + elem!( + storage_buffer_dynamic, + ash::vk::DescriptorType::STORAGE_BUFFER_DYNAMIC + ); + elem!( + uniform_texel_buffer, + ash::vk::DescriptorType::UNIFORM_TEXEL_BUFFER + ); + elem!( + storage_texel_buffer, + ash::vk::DescriptorType::STORAGE_TEXEL_BUFFER + ); + elem!(sampled_image, ash::vk::DescriptorType::SAMPLED_IMAGE); + elem!(storage_image, ash::vk::DescriptorType::STORAGE_IMAGE); + elem!(sampler, ash::vk::DescriptorType::SAMPLER); + elem!( + combined_image_sampler, + ash::vk::DescriptorType::COMBINED_IMAGE_SAMPLER + ); + elem!(input_attachment, ash::vk::DescriptorType::INPUT_ATTACHMENT); + + assert!( + !pool_sizes.is_empty(), + "All the descriptors count of a pool are 0" + ); + + let pool = unsafe { + let infos = ash::vk::DescriptorPoolCreateInfo { + flags: if free_descriptor_set_bit { + ash::vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET + } else { + ash::vk::DescriptorPoolCreateFlags::empty() + }, + max_sets: max_sets, + pool_size_count: pool_sizes.len() as u32, + p_pool_sizes: pool_sizes.as_ptr(), + ..Default::default() + }; + + let mut output = MaybeUninit::uninit(); + check_errors(fns.v1_0.create_descriptor_pool( + device.internal_object(), + &infos, + ptr::null(), + output.as_mut_ptr(), + ))?; + output.assume_init() + }; + + Ok(UnsafeDescriptorPool { + pool, + device: device.clone(), + }) + } + + /// Allocates descriptor sets from the pool, one for each layout. + /// Returns an iterator to the allocated sets, or an error. + /// + /// The `FragmentedPool` errors often can't be prevented. If the function returns this error, + /// you should just create a new pool. + /// + /// # Panic + /// + /// - Panics if one of the layouts wasn't created with the same device as the pool. + /// + /// # Safety + /// + /// See also the `new` function. + /// + /// - The total descriptors of the layouts must fit in the pool. + /// - The total number of descriptor sets allocated from the pool must not overflow the pool. + /// - You must ensure that the allocated descriptor sets are no longer in use when the pool + /// is destroyed, as destroying the pool is equivalent to freeing all the sets. + /// + #[inline] + pub unsafe fn alloc<'l, I>( + &mut self, + layouts: I, + ) -> Result<UnsafeDescriptorPoolAllocIter, DescriptorPoolAllocError> + where + I: IntoIterator<Item = &'l DescriptorSetLayout>, + { + let layouts: SmallVec<[_; 8]> = layouts + .into_iter() + .map(|l| { + assert_eq!( + self.device.internal_object(), + l.device().internal_object(), + "Tried to allocate from a pool with a set layout of a different \ + device" + ); + l.internal_object() + }) + .collect(); + + self.alloc_impl(&layouts) + } + + // Actual implementation of `alloc`. Separated so that it is not inlined. + unsafe fn alloc_impl( + &mut self, + layouts: &SmallVec<[ash::vk::DescriptorSetLayout; 8]>, + ) -> Result<UnsafeDescriptorPoolAllocIter, DescriptorPoolAllocError> { + let num = layouts.len(); + + if num == 0 { + return Ok(UnsafeDescriptorPoolAllocIter { + sets: vec![].into_iter(), + }); + } + + let infos = ash::vk::DescriptorSetAllocateInfo { + descriptor_pool: self.pool, + descriptor_set_count: layouts.len() as u32, + p_set_layouts: layouts.as_ptr(), + ..Default::default() + }; + + let mut output = Vec::with_capacity(num); + + let fns = self.device.fns(); + let ret = fns.v1_0.allocate_descriptor_sets( + self.device.internal_object(), + &infos, + output.as_mut_ptr(), + ); + + // According to the specs, because `VK_ERROR_FRAGMENTED_POOL` was added after version + // 1.0 of Vulkan, any negative return value except out-of-memory errors must be + // considered as a fragmented pool error. + match ret { + ash::vk::Result::ERROR_OUT_OF_HOST_MEMORY => { + return Err(DescriptorPoolAllocError::OutOfHostMemory); + } + ash::vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => { + return Err(DescriptorPoolAllocError::OutOfDeviceMemory); + } + ash::vk::Result::ERROR_OUT_OF_POOL_MEMORY_KHR => { + return Err(DescriptorPoolAllocError::OutOfPoolMemory); + } + c if c.as_raw() < 0 => { + return Err(DescriptorPoolAllocError::FragmentedPool); + } + _ => (), + }; + + output.set_len(num); + + Ok(UnsafeDescriptorPoolAllocIter { + sets: output.into_iter(), + }) + } + + /// Frees some descriptor sets. + /// + /// Note that it is not mandatory to free sets. Destroying or resetting the pool destroys all + /// the descriptor sets. + /// + /// # Safety + /// + /// - The pool must have been created with `free_descriptor_set_bit` set to `true`. + /// - The descriptor sets must have been allocated from the pool. + /// - The descriptor sets must not be free'd twice. + /// - The descriptor sets must not be in use by the GPU. + /// + #[inline] + pub unsafe fn free<I>(&mut self, descriptor_sets: I) -> Result<(), OomError> + where + I: IntoIterator<Item = UnsafeDescriptorSet>, + { + let sets: SmallVec<[_; 8]> = descriptor_sets + .into_iter() + .map(|s| s.internal_object()) + .collect(); + if !sets.is_empty() { + self.free_impl(&sets) + } else { + Ok(()) + } + } + + // Actual implementation of `free`. Separated so that it is not inlined. + unsafe fn free_impl( + &mut self, + sets: &SmallVec<[ash::vk::DescriptorSet; 8]>, + ) -> Result<(), OomError> { + let fns = self.device.fns(); + check_errors(fns.v1_0.free_descriptor_sets( + self.device.internal_object(), + self.pool, + sets.len() as u32, + sets.as_ptr(), + ))?; + Ok(()) + } + + /// Resets the pool. + /// + /// This destroys all descriptor sets and empties the pool. + pub unsafe fn reset(&mut self) -> Result<(), OomError> { + let fns = self.device.fns(); + check_errors(fns.v1_0.reset_descriptor_pool( + self.device.internal_object(), + self.pool, + ash::vk::DescriptorPoolResetFlags::empty(), + ))?; + Ok(()) + } +} + +unsafe impl DeviceOwned for UnsafeDescriptorPool { + #[inline] + fn device(&self) -> &Arc<Device> { + &self.device + } +} + +impl fmt::Debug for UnsafeDescriptorPool { + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + fmt.debug_struct("UnsafeDescriptorPool") + .field("raw", &self.pool) + .field("device", &self.device) + .finish() + } +} + +impl Drop for UnsafeDescriptorPool { + #[inline] + fn drop(&mut self) { + unsafe { + let fns = self.device.fns(); + fns.v1_0 + .destroy_descriptor_pool(self.device.internal_object(), self.pool, ptr::null()); + } + } +} + +/// Error that can be returned when creating a device. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum DescriptorPoolAllocError { + /// There is no memory available on the host (ie. the CPU, RAM, etc.). + OutOfHostMemory, + /// There is no memory available on the device (ie. video memory). + OutOfDeviceMemory, + /// Allocation has failed because the pool is too fragmented. + FragmentedPool, + /// There is no more space available in the descriptor pool. + OutOfPoolMemory, +} + +impl error::Error for DescriptorPoolAllocError {} + +impl fmt::Display for DescriptorPoolAllocError { + #[inline] + fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!( + fmt, + "{}", + match *self { + DescriptorPoolAllocError::OutOfHostMemory => "no memory available on the host", + DescriptorPoolAllocError::OutOfDeviceMemory => { + "no memory available on the graphical device" + } + DescriptorPoolAllocError::FragmentedPool => { + "allocation has failed because the pool is too fragmented" + } + DescriptorPoolAllocError::OutOfPoolMemory => { + "there is no more space available in the descriptor pool" + } + } + ) + } +} + +/// Iterator to the descriptor sets allocated from an unsafe descriptor pool. +#[derive(Debug)] +pub struct UnsafeDescriptorPoolAllocIter { + sets: VecIntoIter<ash::vk::DescriptorSet>, +} + +impl Iterator for UnsafeDescriptorPoolAllocIter { + type Item = UnsafeDescriptorSet; + + #[inline] + fn next(&mut self) -> Option<UnsafeDescriptorSet> { + self.sets.next().map(|s| UnsafeDescriptorSet { set: s }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option<usize>) { + self.sets.size_hint() + } +} + +impl ExactSizeIterator for UnsafeDescriptorPoolAllocIter {} + +#[cfg(test)] +mod tests { + use crate::descriptor_set::layout::DescriptorBufferDesc; + use crate::descriptor_set::layout::DescriptorDesc; + use crate::descriptor_set::layout::DescriptorDescTy; + use crate::descriptor_set::layout::DescriptorSetDesc; + use crate::descriptor_set::layout::DescriptorSetLayout; + use crate::descriptor_set::pool::DescriptorsCount; + use crate::descriptor_set::pool::UnsafeDescriptorPool; + use crate::pipeline::shader::ShaderStages; + use std::iter; + + #[test] + fn pool_create() { + let (device, _) = gfx_dev_and_queue!(); + let desc = DescriptorsCount { + uniform_buffer: 1, + ..DescriptorsCount::zero() + }; + + let _ = UnsafeDescriptorPool::new(device, &desc, 10, false).unwrap(); + } + + #[test] + fn zero_max_set() { + let (device, _) = gfx_dev_and_queue!(); + let desc = DescriptorsCount { + uniform_buffer: 1, + ..DescriptorsCount::zero() + }; + + assert_should_panic!("The maximum number of sets can't be 0", { + let _ = UnsafeDescriptorPool::new(device, &desc, 0, false); + }); + } + + #[test] + fn zero_descriptors() { + let (device, _) = gfx_dev_and_queue!(); + + assert_should_panic!("All the descriptors count of a pool are 0", { + let _ = UnsafeDescriptorPool::new(device, &DescriptorsCount::zero(), 10, false); + }); + } + + #[test] + fn basic_alloc() { + let (device, _) = gfx_dev_and_queue!(); + + let layout = DescriptorDesc { + ty: DescriptorDescTy::Buffer(DescriptorBufferDesc { + dynamic: Some(false), + storage: false, + }), + array_count: 1, + stages: ShaderStages::all_graphics(), + readonly: true, + }; + + let set_layout = DescriptorSetLayout::new( + device.clone(), + DescriptorSetDesc::new(iter::once(Some(layout))), + ) + .unwrap(); + + let desc = DescriptorsCount { + uniform_buffer: 10, + ..DescriptorsCount::zero() + }; + + let mut pool = UnsafeDescriptorPool::new(device, &desc, 10, false).unwrap(); + unsafe { + let sets = pool.alloc(iter::once(&set_layout)).unwrap(); + assert_eq!(sets.count(), 1); + } + } + + #[test] + fn alloc_diff_device() { + let (device1, _) = gfx_dev_and_queue!(); + let (device2, _) = gfx_dev_and_queue!(); + + let layout = DescriptorDesc { + ty: DescriptorDescTy::Buffer(DescriptorBufferDesc { + dynamic: Some(false), + storage: false, + }), + array_count: 1, + stages: ShaderStages::all_graphics(), + readonly: true, + }; + + let set_layout = + DescriptorSetLayout::new(device1, DescriptorSetDesc::new(iter::once(Some(layout)))) + .unwrap(); + + let desc = DescriptorsCount { + uniform_buffer: 10, + ..DescriptorsCount::zero() + }; + + assert_should_panic!( + "Tried to allocate from a pool with a set layout \ + of a different device", + { + let mut pool = UnsafeDescriptorPool::new(device2, &desc, 10, false).unwrap(); + + unsafe { + let _ = pool.alloc(iter::once(&set_layout)); + } + } + ); + } + + #[test] + fn alloc_zero() { + let (device, _) = gfx_dev_and_queue!(); + + let desc = DescriptorsCount { + uniform_buffer: 1, + ..DescriptorsCount::zero() + }; + + let mut pool = UnsafeDescriptorPool::new(device, &desc, 1, false).unwrap(); + unsafe { + let sets = pool.alloc(iter::empty()).unwrap(); + assert_eq!(sets.count(), 0); + } + } +} |