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