• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2021 The vulkano developers
2 // Licensed under the Apache License, Version 2.0
3 // <LICENSE-APACHE or
4 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT
5 // license <LICENSE-MIT or https://opensource.org/licenses/MIT>,
6 // at your option. All files in the project carrying such
7 // notice may not be copied, modified, or distributed except
8 // according to those terms.
9 
10 use crate::check_errors;
11 use crate::descriptor_set::layout::DescriptorSetLayout;
12 use crate::descriptor_set::pool::DescriptorsCount;
13 use crate::descriptor_set::UnsafeDescriptorSet;
14 use crate::device::Device;
15 use crate::device::DeviceOwned;
16 use crate::OomError;
17 use crate::VulkanObject;
18 use smallvec::SmallVec;
19 use std::error;
20 use std::fmt;
21 use std::mem::MaybeUninit;
22 use std::ptr;
23 use std::sync::Arc;
24 use std::vec::IntoIter as VecIntoIter;
25 
26 /// Pool from which descriptor sets are allocated from.
27 ///
28 /// A pool has a maximum number of descriptor sets and a maximum number of descriptors (one value
29 /// per descriptor type) it can allocate.
30 pub struct UnsafeDescriptorPool {
31     pool: ash::vk::DescriptorPool,
32     device: Arc<Device>,
33 }
34 
35 impl UnsafeDescriptorPool {
36     /// Initializes a new pool.
37     ///
38     /// Initializes a pool whose capacity is given by `count` and `max_sets`. At most `count`
39     /// descriptors or `max_sets` descriptor sets can be allocated at once with this pool.
40     ///
41     /// If `free_descriptor_set_bit` is `true`, then individual descriptor sets can be free'd from
42     /// the pool. Otherwise you must reset or destroy the whole pool at once.
43     ///
44     /// # Panic
45     ///
46     /// - Panics if all the descriptors count are 0.
47     /// - Panics if `max_sets` is 0.
48     ///
new( device: Arc<Device>, count: &DescriptorsCount, max_sets: u32, free_descriptor_set_bit: bool, ) -> Result<UnsafeDescriptorPool, OomError>49     pub fn new(
50         device: Arc<Device>,
51         count: &DescriptorsCount,
52         max_sets: u32,
53         free_descriptor_set_bit: bool,
54     ) -> Result<UnsafeDescriptorPool, OomError> {
55         let fns = device.fns();
56 
57         assert_ne!(max_sets, 0, "The maximum number of sets can't be 0");
58 
59         let mut pool_sizes: SmallVec<[_; 10]> = SmallVec::new();
60 
61         macro_rules! elem {
62             ($field:ident, $ty:expr) => {
63                 if count.$field >= 1 {
64                     pool_sizes.push(ash::vk::DescriptorPoolSize {
65                         ty: $ty,
66                         descriptor_count: count.$field,
67                     });
68                 }
69             };
70         }
71 
72         elem!(uniform_buffer, ash::vk::DescriptorType::UNIFORM_BUFFER);
73         elem!(storage_buffer, ash::vk::DescriptorType::STORAGE_BUFFER);
74         elem!(
75             uniform_buffer_dynamic,
76             ash::vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC
77         );
78         elem!(
79             storage_buffer_dynamic,
80             ash::vk::DescriptorType::STORAGE_BUFFER_DYNAMIC
81         );
82         elem!(
83             uniform_texel_buffer,
84             ash::vk::DescriptorType::UNIFORM_TEXEL_BUFFER
85         );
86         elem!(
87             storage_texel_buffer,
88             ash::vk::DescriptorType::STORAGE_TEXEL_BUFFER
89         );
90         elem!(sampled_image, ash::vk::DescriptorType::SAMPLED_IMAGE);
91         elem!(storage_image, ash::vk::DescriptorType::STORAGE_IMAGE);
92         elem!(sampler, ash::vk::DescriptorType::SAMPLER);
93         elem!(
94             combined_image_sampler,
95             ash::vk::DescriptorType::COMBINED_IMAGE_SAMPLER
96         );
97         elem!(input_attachment, ash::vk::DescriptorType::INPUT_ATTACHMENT);
98 
99         assert!(
100             !pool_sizes.is_empty(),
101             "All the descriptors count of a pool are 0"
102         );
103 
104         let pool = unsafe {
105             let infos = ash::vk::DescriptorPoolCreateInfo {
106                 flags: if free_descriptor_set_bit {
107                     ash::vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET
108                 } else {
109                     ash::vk::DescriptorPoolCreateFlags::empty()
110                 },
111                 max_sets: max_sets,
112                 pool_size_count: pool_sizes.len() as u32,
113                 p_pool_sizes: pool_sizes.as_ptr(),
114                 ..Default::default()
115             };
116 
117             let mut output = MaybeUninit::uninit();
118             check_errors(fns.v1_0.create_descriptor_pool(
119                 device.internal_object(),
120                 &infos,
121                 ptr::null(),
122                 output.as_mut_ptr(),
123             ))?;
124             output.assume_init()
125         };
126 
127         Ok(UnsafeDescriptorPool {
128             pool,
129             device: device.clone(),
130         })
131     }
132 
133     /// Allocates descriptor sets from the pool, one for each layout.
134     /// Returns an iterator to the allocated sets, or an error.
135     ///
136     /// The `FragmentedPool` errors often can't be prevented. If the function returns this error,
137     /// you should just create a new pool.
138     ///
139     /// # Panic
140     ///
141     /// - Panics if one of the layouts wasn't created with the same device as the pool.
142     ///
143     /// # Safety
144     ///
145     /// See also the `new` function.
146     ///
147     /// - The total descriptors of the layouts must fit in the pool.
148     /// - The total number of descriptor sets allocated from the pool must not overflow the pool.
149     /// - You must ensure that the allocated descriptor sets are no longer in use when the pool
150     ///   is destroyed, as destroying the pool is equivalent to freeing all the sets.
151     ///
152     #[inline]
alloc<'l, I>( &mut self, layouts: I, ) -> Result<UnsafeDescriptorPoolAllocIter, DescriptorPoolAllocError> where I: IntoIterator<Item = &'l DescriptorSetLayout>,153     pub unsafe fn alloc<'l, I>(
154         &mut self,
155         layouts: I,
156     ) -> Result<UnsafeDescriptorPoolAllocIter, DescriptorPoolAllocError>
157     where
158         I: IntoIterator<Item = &'l DescriptorSetLayout>,
159     {
160         let layouts: SmallVec<[_; 8]> = layouts
161             .into_iter()
162             .map(|l| {
163                 assert_eq!(
164                     self.device.internal_object(),
165                     l.device().internal_object(),
166                     "Tried to allocate from a pool with a set layout of a different \
167                                  device"
168                 );
169                 l.internal_object()
170             })
171             .collect();
172 
173         self.alloc_impl(&layouts)
174     }
175 
176     // Actual implementation of `alloc`. Separated so that it is not inlined.
alloc_impl( &mut self, layouts: &SmallVec<[ash::vk::DescriptorSetLayout; 8]>, ) -> Result<UnsafeDescriptorPoolAllocIter, DescriptorPoolAllocError>177     unsafe fn alloc_impl(
178         &mut self,
179         layouts: &SmallVec<[ash::vk::DescriptorSetLayout; 8]>,
180     ) -> Result<UnsafeDescriptorPoolAllocIter, DescriptorPoolAllocError> {
181         let num = layouts.len();
182 
183         if num == 0 {
184             return Ok(UnsafeDescriptorPoolAllocIter {
185                 sets: vec![].into_iter(),
186             });
187         }
188 
189         let infos = ash::vk::DescriptorSetAllocateInfo {
190             descriptor_pool: self.pool,
191             descriptor_set_count: layouts.len() as u32,
192             p_set_layouts: layouts.as_ptr(),
193             ..Default::default()
194         };
195 
196         let mut output = Vec::with_capacity(num);
197 
198         let fns = self.device.fns();
199         let ret = fns.v1_0.allocate_descriptor_sets(
200             self.device.internal_object(),
201             &infos,
202             output.as_mut_ptr(),
203         );
204 
205         // According to the specs, because `VK_ERROR_FRAGMENTED_POOL` was added after version
206         // 1.0 of Vulkan, any negative return value except out-of-memory errors must be
207         // considered as a fragmented pool error.
208         match ret {
209             ash::vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
210                 return Err(DescriptorPoolAllocError::OutOfHostMemory);
211             }
212             ash::vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
213                 return Err(DescriptorPoolAllocError::OutOfDeviceMemory);
214             }
215             ash::vk::Result::ERROR_OUT_OF_POOL_MEMORY_KHR => {
216                 return Err(DescriptorPoolAllocError::OutOfPoolMemory);
217             }
218             c if c.as_raw() < 0 => {
219                 return Err(DescriptorPoolAllocError::FragmentedPool);
220             }
221             _ => (),
222         };
223 
224         output.set_len(num);
225 
226         Ok(UnsafeDescriptorPoolAllocIter {
227             sets: output.into_iter(),
228         })
229     }
230 
231     /// Frees some descriptor sets.
232     ///
233     /// Note that it is not mandatory to free sets. Destroying or resetting the pool destroys all
234     /// the descriptor sets.
235     ///
236     /// # Safety
237     ///
238     /// - The pool must have been created with `free_descriptor_set_bit` set to `true`.
239     /// - The descriptor sets must have been allocated from the pool.
240     /// - The descriptor sets must not be free'd twice.
241     /// - The descriptor sets must not be in use by the GPU.
242     ///
243     #[inline]
free<I>(&mut self, descriptor_sets: I) -> Result<(), OomError> where I: IntoIterator<Item = UnsafeDescriptorSet>,244     pub unsafe fn free<I>(&mut self, descriptor_sets: I) -> Result<(), OomError>
245     where
246         I: IntoIterator<Item = UnsafeDescriptorSet>,
247     {
248         let sets: SmallVec<[_; 8]> = descriptor_sets
249             .into_iter()
250             .map(|s| s.internal_object())
251             .collect();
252         if !sets.is_empty() {
253             self.free_impl(&sets)
254         } else {
255             Ok(())
256         }
257     }
258 
259     // Actual implementation of `free`. Separated so that it is not inlined.
free_impl( &mut self, sets: &SmallVec<[ash::vk::DescriptorSet; 8]>, ) -> Result<(), OomError>260     unsafe fn free_impl(
261         &mut self,
262         sets: &SmallVec<[ash::vk::DescriptorSet; 8]>,
263     ) -> Result<(), OomError> {
264         let fns = self.device.fns();
265         check_errors(fns.v1_0.free_descriptor_sets(
266             self.device.internal_object(),
267             self.pool,
268             sets.len() as u32,
269             sets.as_ptr(),
270         ))?;
271         Ok(())
272     }
273 
274     /// Resets the pool.
275     ///
276     /// This destroys all descriptor sets and empties the pool.
reset(&mut self) -> Result<(), OomError>277     pub unsafe fn reset(&mut self) -> Result<(), OomError> {
278         let fns = self.device.fns();
279         check_errors(fns.v1_0.reset_descriptor_pool(
280             self.device.internal_object(),
281             self.pool,
282             ash::vk::DescriptorPoolResetFlags::empty(),
283         ))?;
284         Ok(())
285     }
286 }
287 
288 unsafe impl DeviceOwned for UnsafeDescriptorPool {
289     #[inline]
device(&self) -> &Arc<Device>290     fn device(&self) -> &Arc<Device> {
291         &self.device
292     }
293 }
294 
295 impl fmt::Debug for UnsafeDescriptorPool {
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>296     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
297         fmt.debug_struct("UnsafeDescriptorPool")
298             .field("raw", &self.pool)
299             .field("device", &self.device)
300             .finish()
301     }
302 }
303 
304 impl Drop for UnsafeDescriptorPool {
305     #[inline]
drop(&mut self)306     fn drop(&mut self) {
307         unsafe {
308             let fns = self.device.fns();
309             fns.v1_0
310                 .destroy_descriptor_pool(self.device.internal_object(), self.pool, ptr::null());
311         }
312     }
313 }
314 
315 /// Error that can be returned when creating a device.
316 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
317 pub enum DescriptorPoolAllocError {
318     /// There is no memory available on the host (ie. the CPU, RAM, etc.).
319     OutOfHostMemory,
320     /// There is no memory available on the device (ie. video memory).
321     OutOfDeviceMemory,
322     /// Allocation has failed because the pool is too fragmented.
323     FragmentedPool,
324     /// There is no more space available in the descriptor pool.
325     OutOfPoolMemory,
326 }
327 
328 impl error::Error for DescriptorPoolAllocError {}
329 
330 impl fmt::Display for DescriptorPoolAllocError {
331     #[inline]
fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error>332     fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
333         write!(
334             fmt,
335             "{}",
336             match *self {
337                 DescriptorPoolAllocError::OutOfHostMemory => "no memory available on the host",
338                 DescriptorPoolAllocError::OutOfDeviceMemory => {
339                     "no memory available on the graphical device"
340                 }
341                 DescriptorPoolAllocError::FragmentedPool => {
342                     "allocation has failed because the pool is too fragmented"
343                 }
344                 DescriptorPoolAllocError::OutOfPoolMemory => {
345                     "there is no more space available in the descriptor pool"
346                 }
347             }
348         )
349     }
350 }
351 
352 /// Iterator to the descriptor sets allocated from an unsafe descriptor pool.
353 #[derive(Debug)]
354 pub struct UnsafeDescriptorPoolAllocIter {
355     sets: VecIntoIter<ash::vk::DescriptorSet>,
356 }
357 
358 impl Iterator for UnsafeDescriptorPoolAllocIter {
359     type Item = UnsafeDescriptorSet;
360 
361     #[inline]
next(&mut self) -> Option<UnsafeDescriptorSet>362     fn next(&mut self) -> Option<UnsafeDescriptorSet> {
363         self.sets.next().map(|s| UnsafeDescriptorSet { set: s })
364     }
365 
366     #[inline]
size_hint(&self) -> (usize, Option<usize>)367     fn size_hint(&self) -> (usize, Option<usize>) {
368         self.sets.size_hint()
369     }
370 }
371 
372 impl ExactSizeIterator for UnsafeDescriptorPoolAllocIter {}
373 
374 #[cfg(test)]
375 mod tests {
376     use crate::descriptor_set::layout::DescriptorBufferDesc;
377     use crate::descriptor_set::layout::DescriptorDesc;
378     use crate::descriptor_set::layout::DescriptorDescTy;
379     use crate::descriptor_set::layout::DescriptorSetDesc;
380     use crate::descriptor_set::layout::DescriptorSetLayout;
381     use crate::descriptor_set::pool::DescriptorsCount;
382     use crate::descriptor_set::pool::UnsafeDescriptorPool;
383     use crate::pipeline::shader::ShaderStages;
384     use std::iter;
385 
386     #[test]
pool_create()387     fn pool_create() {
388         let (device, _) = gfx_dev_and_queue!();
389         let desc = DescriptorsCount {
390             uniform_buffer: 1,
391             ..DescriptorsCount::zero()
392         };
393 
394         let _ = UnsafeDescriptorPool::new(device, &desc, 10, false).unwrap();
395     }
396 
397     #[test]
zero_max_set()398     fn zero_max_set() {
399         let (device, _) = gfx_dev_and_queue!();
400         let desc = DescriptorsCount {
401             uniform_buffer: 1,
402             ..DescriptorsCount::zero()
403         };
404 
405         assert_should_panic!("The maximum number of sets can't be 0", {
406             let _ = UnsafeDescriptorPool::new(device, &desc, 0, false);
407         });
408     }
409 
410     #[test]
zero_descriptors()411     fn zero_descriptors() {
412         let (device, _) = gfx_dev_and_queue!();
413 
414         assert_should_panic!("All the descriptors count of a pool are 0", {
415             let _ = UnsafeDescriptorPool::new(device, &DescriptorsCount::zero(), 10, false);
416         });
417     }
418 
419     #[test]
basic_alloc()420     fn basic_alloc() {
421         let (device, _) = gfx_dev_and_queue!();
422 
423         let layout = DescriptorDesc {
424             ty: DescriptorDescTy::Buffer(DescriptorBufferDesc {
425                 dynamic: Some(false),
426                 storage: false,
427             }),
428             array_count: 1,
429             stages: ShaderStages::all_graphics(),
430             readonly: true,
431         };
432 
433         let set_layout = DescriptorSetLayout::new(
434             device.clone(),
435             DescriptorSetDesc::new(iter::once(Some(layout))),
436         )
437         .unwrap();
438 
439         let desc = DescriptorsCount {
440             uniform_buffer: 10,
441             ..DescriptorsCount::zero()
442         };
443 
444         let mut pool = UnsafeDescriptorPool::new(device, &desc, 10, false).unwrap();
445         unsafe {
446             let sets = pool.alloc(iter::once(&set_layout)).unwrap();
447             assert_eq!(sets.count(), 1);
448         }
449     }
450 
451     #[test]
alloc_diff_device()452     fn alloc_diff_device() {
453         let (device1, _) = gfx_dev_and_queue!();
454         let (device2, _) = gfx_dev_and_queue!();
455 
456         let layout = DescriptorDesc {
457             ty: DescriptorDescTy::Buffer(DescriptorBufferDesc {
458                 dynamic: Some(false),
459                 storage: false,
460             }),
461             array_count: 1,
462             stages: ShaderStages::all_graphics(),
463             readonly: true,
464         };
465 
466         let set_layout =
467             DescriptorSetLayout::new(device1, DescriptorSetDesc::new(iter::once(Some(layout))))
468                 .unwrap();
469 
470         let desc = DescriptorsCount {
471             uniform_buffer: 10,
472             ..DescriptorsCount::zero()
473         };
474 
475         assert_should_panic!(
476             "Tried to allocate from a pool with a set layout \
477                               of a different device",
478             {
479                 let mut pool = UnsafeDescriptorPool::new(device2, &desc, 10, false).unwrap();
480 
481                 unsafe {
482                     let _ = pool.alloc(iter::once(&set_layout));
483                 }
484             }
485         );
486     }
487 
488     #[test]
alloc_zero()489     fn alloc_zero() {
490         let (device, _) = gfx_dev_and_queue!();
491 
492         let desc = DescriptorsCount {
493             uniform_buffer: 1,
494             ..DescriptorsCount::zero()
495         };
496 
497         let mut pool = UnsafeDescriptorPool::new(device, &desc, 1, false).unwrap();
498         unsafe {
499             let sets = pool.alloc(iter::empty()).unwrap();
500             assert_eq!(sets.count(), 0);
501         }
502     }
503 }
504