• 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::{
11     descriptor_set::{
12         layout::{DescriptorSetLayout, DescriptorType},
13         sys::UnsafeDescriptorSet,
14     },
15     device::{Device, DeviceOwned},
16     macros::impl_id_counter,
17     OomError, Version, VulkanError, VulkanObject,
18 };
19 use ahash::HashMap;
20 use smallvec::SmallVec;
21 use std::{
22     cell::Cell,
23     error::Error,
24     fmt::{Display, Error as FmtError, Formatter},
25     marker::PhantomData,
26     mem::MaybeUninit,
27     num::NonZeroU64,
28     ptr,
29     sync::Arc,
30 };
31 
32 /// Pool that descriptors are allocated from.
33 ///
34 /// A pool has a maximum number of descriptor sets and a maximum number of descriptors (one value
35 /// per descriptor type) it can allocate.
36 #[derive(Debug)]
37 pub struct DescriptorPool {
38     handle: ash::vk::DescriptorPool,
39     device: Arc<Device>,
40     id: NonZeroU64,
41 
42     max_sets: u32,
43     pool_sizes: HashMap<DescriptorType, u32>,
44     can_free_descriptor_sets: bool,
45     // Unimplement `Sync`, as Vulkan descriptor pools are not thread safe.
46     _marker: PhantomData<Cell<ash::vk::DescriptorPool>>,
47 }
48 
49 impl DescriptorPool {
50     /// Creates a new `UnsafeDescriptorPool`.
51     ///
52     /// # Panics
53     ///
54     /// - Panics if `create_info.max_sets` is `0`.
55     /// - Panics if `create_info.pool_sizes` is empty.
56     /// - Panics if `create_info.pool_sizes` contains a descriptor type with a count of `0`.
new( device: Arc<Device>, create_info: DescriptorPoolCreateInfo, ) -> Result<DescriptorPool, OomError>57     pub fn new(
58         device: Arc<Device>,
59         create_info: DescriptorPoolCreateInfo,
60     ) -> Result<DescriptorPool, OomError> {
61         let DescriptorPoolCreateInfo {
62             max_sets,
63             pool_sizes,
64             can_free_descriptor_sets,
65             _ne: _,
66         } = create_info;
67 
68         // VUID-VkDescriptorPoolCreateInfo-maxSets-00301
69         assert!(max_sets != 0);
70 
71         // VUID-VkDescriptorPoolCreateInfo-poolSizeCount-arraylength
72         assert!(!pool_sizes.is_empty());
73 
74         let handle = {
75             let pool_sizes: SmallVec<[_; 8]> = pool_sizes
76                 .iter()
77                 .map(|(&ty, &descriptor_count)| {
78                     // VUID-VkDescriptorPoolSize-descriptorCount-00302
79                     assert!(descriptor_count != 0);
80 
81                     ash::vk::DescriptorPoolSize {
82                         ty: ty.into(),
83                         descriptor_count,
84                     }
85                 })
86                 .collect();
87 
88             let mut flags = ash::vk::DescriptorPoolCreateFlags::empty();
89 
90             if can_free_descriptor_sets {
91                 flags |= ash::vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET;
92             }
93 
94             let create_info = ash::vk::DescriptorPoolCreateInfo {
95                 flags,
96                 max_sets,
97                 pool_size_count: pool_sizes.len() as u32,
98                 p_pool_sizes: pool_sizes.as_ptr(),
99                 ..Default::default()
100             };
101 
102             unsafe {
103                 let fns = device.fns();
104                 let mut output = MaybeUninit::uninit();
105                 (fns.v1_0.create_descriptor_pool)(
106                     device.handle(),
107                     &create_info,
108                     ptr::null(),
109                     output.as_mut_ptr(),
110                 )
111                 .result()
112                 .map_err(VulkanError::from)?;
113                 output.assume_init()
114             }
115         };
116 
117         Ok(DescriptorPool {
118             handle,
119             device,
120             id: Self::next_id(),
121             max_sets,
122             pool_sizes,
123             can_free_descriptor_sets,
124             _marker: PhantomData,
125         })
126     }
127 
128     /// Creates a new `UnsafeDescriptorPool` from a raw object handle.
129     ///
130     /// # Safety
131     ///
132     /// - `handle` must be a valid Vulkan object handle created from `device`.
133     /// - `create_info` must match the info used to create the object.
134     #[inline]
from_handle( device: Arc<Device>, handle: ash::vk::DescriptorPool, create_info: DescriptorPoolCreateInfo, ) -> DescriptorPool135     pub unsafe fn from_handle(
136         device: Arc<Device>,
137         handle: ash::vk::DescriptorPool,
138         create_info: DescriptorPoolCreateInfo,
139     ) -> DescriptorPool {
140         let DescriptorPoolCreateInfo {
141             max_sets,
142             pool_sizes,
143             can_free_descriptor_sets,
144             _ne: _,
145         } = create_info;
146 
147         DescriptorPool {
148             handle,
149             device,
150             id: Self::next_id(),
151             max_sets,
152             pool_sizes,
153             can_free_descriptor_sets,
154             _marker: PhantomData,
155         }
156     }
157 
158     /// Returns the maximum number of sets that can be allocated from the pool.
159     #[inline]
max_sets(&self) -> u32160     pub fn max_sets(&self) -> u32 {
161         self.max_sets
162     }
163 
164     /// Returns the number of descriptors of each type that the pool was created with.
165     #[inline]
pool_sizes(&self) -> &HashMap<DescriptorType, u32>166     pub fn pool_sizes(&self) -> &HashMap<DescriptorType, u32> {
167         &self.pool_sizes
168     }
169 
170     /// Returns whether the descriptor sets allocated from the pool can be individually freed.
171     #[inline]
can_free_descriptor_sets(&self) -> bool172     pub fn can_free_descriptor_sets(&self) -> bool {
173         self.can_free_descriptor_sets
174     }
175 
176     /// Allocates descriptor sets from the pool, one for each element in `create_info`.
177     /// Returns an iterator to the allocated sets, or an error.
178     ///
179     /// The `FragmentedPool` errors often can't be prevented. If the function returns this error,
180     /// you should just create a new pool.
181     ///
182     /// # Panics
183     ///
184     /// - Panics if one of the layouts wasn't created with the same device as the pool.
185     ///
186     /// # Safety
187     ///
188     /// See also the `new` function.
189     ///
190     /// - The total descriptors of the layouts must fit in the pool.
191     /// - The total number of descriptor sets allocated from the pool must not overflow the pool.
192     /// - You must ensure that the allocated descriptor sets are no longer in use when the pool
193     ///   is destroyed, as destroying the pool is equivalent to freeing all the sets.
allocate_descriptor_sets<'a>( &self, allocate_info: impl IntoIterator<Item = DescriptorSetAllocateInfo<'a>>, ) -> Result<impl ExactSizeIterator<Item = UnsafeDescriptorSet>, DescriptorPoolAllocError>194     pub unsafe fn allocate_descriptor_sets<'a>(
195         &self,
196         allocate_info: impl IntoIterator<Item = DescriptorSetAllocateInfo<'a>>,
197     ) -> Result<impl ExactSizeIterator<Item = UnsafeDescriptorSet>, DescriptorPoolAllocError> {
198         let (layouts, variable_descriptor_counts): (SmallVec<[_; 1]>, SmallVec<[_; 1]>) =
199             allocate_info
200                 .into_iter()
201                 .map(|info| {
202                     assert_eq!(self.device.handle(), info.layout.device().handle(),);
203                     debug_assert!(!info.layout.push_descriptor());
204                     debug_assert!(
205                         info.variable_descriptor_count <= info.layout.variable_descriptor_count()
206                     );
207 
208                     (info.layout.handle(), info.variable_descriptor_count)
209                 })
210                 .unzip();
211 
212         let output = if layouts.is_empty() {
213             vec![]
214         } else {
215             let variable_desc_count_alloc_info = if (self.device.api_version() >= Version::V1_2
216                 || self.device.enabled_extensions().ext_descriptor_indexing)
217                 && variable_descriptor_counts.iter().any(|c| *c != 0)
218             {
219                 Some(ash::vk::DescriptorSetVariableDescriptorCountAllocateInfo {
220                     descriptor_set_count: layouts.len() as u32,
221                     p_descriptor_counts: variable_descriptor_counts.as_ptr(),
222                     ..Default::default()
223                 })
224             } else {
225                 None
226             };
227 
228             let infos = ash::vk::DescriptorSetAllocateInfo {
229                 descriptor_pool: self.handle,
230                 descriptor_set_count: layouts.len() as u32,
231                 p_set_layouts: layouts.as_ptr(),
232                 p_next: if let Some(next) = variable_desc_count_alloc_info.as_ref() {
233                     next as *const _ as *const _
234                 } else {
235                     ptr::null()
236                 },
237                 ..Default::default()
238             };
239 
240             let mut output = Vec::with_capacity(layouts.len());
241 
242             let fns = self.device.fns();
243             let ret = (fns.v1_0.allocate_descriptor_sets)(
244                 self.device.handle(),
245                 &infos,
246                 output.as_mut_ptr(),
247             );
248 
249             // According to the specs, because `VK_ERROR_FRAGMENTED_POOL` was added after version
250             // 1.0 of Vulkan, any negative return value except out-of-memory errors must be
251             // considered as a fragmented pool error.
252             match ret {
253                 ash::vk::Result::ERROR_OUT_OF_HOST_MEMORY => {
254                     return Err(DescriptorPoolAllocError::OutOfHostMemory);
255                 }
256                 ash::vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => {
257                     return Err(DescriptorPoolAllocError::OutOfDeviceMemory);
258                 }
259                 ash::vk::Result::ERROR_OUT_OF_POOL_MEMORY_KHR => {
260                     return Err(DescriptorPoolAllocError::OutOfPoolMemory);
261                 }
262                 c if c.as_raw() < 0 => {
263                     return Err(DescriptorPoolAllocError::FragmentedPool);
264                 }
265                 _ => (),
266             };
267 
268             output.set_len(layouts.len());
269             output
270         };
271 
272         Ok(output.into_iter().map(UnsafeDescriptorSet::new))
273     }
274 
275     /// Frees some descriptor sets.
276     ///
277     /// Note that it is not mandatory to free sets. Destroying or resetting the pool destroys all
278     /// the descriptor sets.
279     ///
280     /// # Safety
281     ///
282     /// - The pool must have been created with `free_descriptor_set_bit` set to `true`.
283     /// - The descriptor sets must have been allocated from the pool.
284     /// - The descriptor sets must not be free'd twice.
285     /// - The descriptor sets must not be in use by the GPU.
free_descriptor_sets( &self, descriptor_sets: impl IntoIterator<Item = UnsafeDescriptorSet>, ) -> Result<(), OomError>286     pub unsafe fn free_descriptor_sets(
287         &self,
288         descriptor_sets: impl IntoIterator<Item = UnsafeDescriptorSet>,
289     ) -> Result<(), OomError> {
290         let sets: SmallVec<[_; 8]> = descriptor_sets.into_iter().map(|s| s.handle()).collect();
291         if !sets.is_empty() {
292             let fns = self.device.fns();
293             (fns.v1_0.free_descriptor_sets)(
294                 self.device.handle(),
295                 self.handle,
296                 sets.len() as u32,
297                 sets.as_ptr(),
298             )
299             .result()
300             .map_err(VulkanError::from)?;
301         }
302 
303         Ok(())
304     }
305 
306     /// Resets the pool.
307     ///
308     /// This destroys all descriptor sets and empties the pool.
309     #[inline]
reset(&self) -> Result<(), OomError>310     pub unsafe fn reset(&self) -> Result<(), OomError> {
311         let fns = self.device.fns();
312         (fns.v1_0.reset_descriptor_pool)(
313             self.device.handle(),
314             self.handle,
315             ash::vk::DescriptorPoolResetFlags::empty(),
316         )
317         .result()
318         .map_err(VulkanError::from)?;
319 
320         Ok(())
321     }
322 }
323 
324 impl Drop for DescriptorPool {
325     #[inline]
drop(&mut self)326     fn drop(&mut self) {
327         unsafe {
328             let fns = self.device.fns();
329             (fns.v1_0.destroy_descriptor_pool)(self.device.handle(), self.handle, ptr::null());
330         }
331     }
332 }
333 
334 unsafe impl VulkanObject for DescriptorPool {
335     type Handle = ash::vk::DescriptorPool;
336 
337     #[inline]
handle(&self) -> Self::Handle338     fn handle(&self) -> Self::Handle {
339         self.handle
340     }
341 }
342 
343 unsafe impl DeviceOwned for DescriptorPool {
344     #[inline]
device(&self) -> &Arc<Device>345     fn device(&self) -> &Arc<Device> {
346         &self.device
347     }
348 }
349 
350 impl_id_counter!(DescriptorPool);
351 
352 /// Parameters to create a new `UnsafeDescriptorPool`.
353 #[derive(Clone, Debug)]
354 pub struct DescriptorPoolCreateInfo {
355     /// The maximum number of descriptor sets that can be allocated from the pool.
356     ///
357     /// The default value is `0`, which must be overridden.
358     pub max_sets: u32,
359 
360     /// The number of descriptors of each type to allocate for the pool.
361     ///
362     /// The default value is empty, which must be overridden.
363     pub pool_sizes: HashMap<DescriptorType, u32>,
364 
365     /// Whether individual descriptor sets can be freed from the pool. Otherwise you must reset or
366     /// destroy the whole pool at once.
367     ///
368     /// The default value is `false`.
369     pub can_free_descriptor_sets: bool,
370 
371     pub _ne: crate::NonExhaustive,
372 }
373 
374 impl Default for DescriptorPoolCreateInfo {
375     #[inline]
default() -> Self376     fn default() -> Self {
377         Self {
378             max_sets: 0,
379             pool_sizes: HashMap::default(),
380             can_free_descriptor_sets: false,
381             _ne: crate::NonExhaustive(()),
382         }
383     }
384 }
385 
386 /// Parameters to allocate a new `UnsafeDescriptorSet` from an `UnsafeDescriptorPool`.
387 #[derive(Clone, Debug)]
388 pub struct DescriptorSetAllocateInfo<'a> {
389     /// The descriptor set layout to create the set for.
390     pub layout: &'a DescriptorSetLayout,
391 
392     /// For layouts with a variable-count binding, the number of descriptors to allocate for that
393     /// binding. This should be 0 for layouts that don't have a variable-count binding.
394     pub variable_descriptor_count: u32,
395 }
396 
397 /// Error that can be returned when creating a device.
398 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
399 pub enum DescriptorPoolAllocError {
400     /// There is no memory available on the host (ie. the CPU, RAM, etc.).
401     OutOfHostMemory,
402     /// There is no memory available on the device (ie. video memory).
403     OutOfDeviceMemory,
404     /// Allocation has failed because the pool is too fragmented.
405     FragmentedPool,
406     /// There is no more space available in the descriptor pool.
407     OutOfPoolMemory,
408 }
409 
410 impl Error for DescriptorPoolAllocError {}
411 
412 impl Display for DescriptorPoolAllocError {
fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError>413     fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), FmtError> {
414         write!(
415             f,
416             "{}",
417             match self {
418                 DescriptorPoolAllocError::OutOfHostMemory => "no memory available on the host",
419                 DescriptorPoolAllocError::OutOfDeviceMemory => {
420                     "no memory available on the graphical device"
421                 }
422                 DescriptorPoolAllocError::FragmentedPool => {
423                     "allocation has failed because the pool is too fragmented"
424                 }
425                 DescriptorPoolAllocError::OutOfPoolMemory => {
426                     "there is no more space available in the descriptor pool"
427                 }
428             }
429         )
430     }
431 }
432 
433 #[cfg(test)]
434 mod tests {
435     use super::{DescriptorPool, DescriptorPoolCreateInfo};
436     use crate::{
437         descriptor_set::{
438             layout::{
439                 DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo,
440                 DescriptorType,
441             },
442             pool::DescriptorSetAllocateInfo,
443         },
444         shader::ShaderStages,
445     };
446 
447     #[test]
pool_create()448     fn pool_create() {
449         let (device, _) = gfx_dev_and_queue!();
450 
451         let _ = DescriptorPool::new(
452             device,
453             DescriptorPoolCreateInfo {
454                 max_sets: 10,
455                 pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
456                 ..Default::default()
457             },
458         )
459         .unwrap();
460     }
461 
462     #[test]
zero_max_set()463     fn zero_max_set() {
464         let (device, _) = gfx_dev_and_queue!();
465 
466         assert_should_panic!({
467             let _ = DescriptorPool::new(
468                 device,
469                 DescriptorPoolCreateInfo {
470                     max_sets: 0,
471                     pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
472                     ..Default::default()
473                 },
474             );
475         });
476     }
477 
478     #[test]
zero_descriptors()479     fn zero_descriptors() {
480         let (device, _) = gfx_dev_and_queue!();
481 
482         assert_should_panic!({
483             let _ = DescriptorPool::new(
484                 device,
485                 DescriptorPoolCreateInfo {
486                     max_sets: 10,
487                     ..Default::default()
488                 },
489             );
490         });
491     }
492 
493     #[test]
basic_alloc()494     fn basic_alloc() {
495         let (device, _) = gfx_dev_and_queue!();
496 
497         let set_layout = DescriptorSetLayout::new(
498             device.clone(),
499             DescriptorSetLayoutCreateInfo {
500                 bindings: [(
501                     0,
502                     DescriptorSetLayoutBinding {
503                         stages: ShaderStages::all_graphics(),
504                         ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
505                     },
506                 )]
507                 .into(),
508                 ..Default::default()
509             },
510         )
511         .unwrap();
512 
513         let pool = DescriptorPool::new(
514             device,
515             DescriptorPoolCreateInfo {
516                 max_sets: 10,
517                 pool_sizes: [(DescriptorType::UniformBuffer, 10)].into_iter().collect(),
518                 ..Default::default()
519             },
520         )
521         .unwrap();
522         unsafe {
523             let sets = pool
524                 .allocate_descriptor_sets([DescriptorSetAllocateInfo {
525                     layout: set_layout.as_ref(),
526                     variable_descriptor_count: 0,
527                 }])
528                 .unwrap();
529             assert_eq!(sets.count(), 1);
530         }
531     }
532 
533     #[test]
alloc_diff_device()534     fn alloc_diff_device() {
535         let (device1, _) = gfx_dev_and_queue!();
536         let (device2, _) = gfx_dev_and_queue!();
537 
538         let set_layout = DescriptorSetLayout::new(
539             device1,
540             DescriptorSetLayoutCreateInfo {
541                 bindings: [(
542                     0,
543                     DescriptorSetLayoutBinding {
544                         stages: ShaderStages::all_graphics(),
545                         ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer)
546                     },
547                 )]
548                 .into(),
549                 ..Default::default()
550             },
551         )
552         .unwrap();
553 
554         assert_should_panic!({
555             let pool = DescriptorPool::new(
556                 device2,
557                 DescriptorPoolCreateInfo {
558                     max_sets: 10,
559                     pool_sizes: [(DescriptorType::UniformBuffer, 10)].into_iter().collect(),
560                     ..Default::default()
561                 },
562             )
563             .unwrap();
564 
565             unsafe {
566                 let _ = pool.allocate_descriptor_sets([DescriptorSetAllocateInfo {
567                     layout: set_layout.as_ref(),
568                     variable_descriptor_count: 0,
569                 }]);
570             }
571         });
572     }
573 
574     #[test]
alloc_zero()575     fn alloc_zero() {
576         let (device, _) = gfx_dev_and_queue!();
577 
578         let pool = DescriptorPool::new(
579             device,
580             DescriptorPoolCreateInfo {
581                 max_sets: 1,
582                 pool_sizes: [(DescriptorType::UniformBuffer, 1)].into_iter().collect(),
583                 ..Default::default()
584             },
585         )
586         .unwrap();
587         unsafe {
588             let sets = pool.allocate_descriptor_sets([]).unwrap();
589             assert_eq!(sets.count(), 0);
590         }
591     }
592 }
593