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