1 // Copyright (c) 2016 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::descriptor_set::layout::DescriptorSetLayout; 11 use crate::descriptor_set::pool::DescriptorPool; 12 use crate::descriptor_set::pool::DescriptorPoolAlloc; 13 use crate::descriptor_set::pool::DescriptorPoolAllocError; 14 use crate::descriptor_set::pool::DescriptorsCount; 15 use crate::descriptor_set::pool::UnsafeDescriptorPool; 16 use crate::descriptor_set::UnsafeDescriptorSet; 17 use crate::device::Device; 18 use crate::device::DeviceOwned; 19 use crate::OomError; 20 use std::sync::Arc; 21 use std::sync::Mutex; 22 23 /// Standard implementation of a descriptor pool. 24 /// 25 /// It is guaranteed that the `Arc<StdDescriptorPool>` is kept alive by its allocations. This is 26 /// desirable so that we can store a `Weak<StdDescriptorPool>`. 27 /// 28 /// Whenever a set is allocated, this implementation will try to find a pool that has some space 29 /// for it. If there is one, allocate from it. If there is none, create a new pool whose capacity 30 /// is 40 sets and 40 times the requested descriptors. This number is arbitrary. 31 pub struct StdDescriptorPool { 32 device: Arc<Device>, 33 pools: Mutex<Vec<Arc<Mutex<Pool>>>>, 34 } 35 36 struct Pool { 37 pool: UnsafeDescriptorPool, 38 remaining_capacity: DescriptorsCount, 39 remaining_sets_count: u32, 40 } 41 42 impl StdDescriptorPool { 43 /// Builds a new `StdDescriptorPool`. new(device: Arc<Device>) -> StdDescriptorPool44 pub fn new(device: Arc<Device>) -> StdDescriptorPool { 45 StdDescriptorPool { 46 device: device, 47 pools: Mutex::new(Vec::new()), 48 } 49 } 50 } 51 52 /// A descriptor set allocated from a `StdDescriptorPool`. 53 pub struct StdDescriptorPoolAlloc { 54 pool: Arc<Mutex<Pool>>, 55 // The set. Inside an option so that we can extract it in the destructor. 56 set: Option<UnsafeDescriptorSet>, 57 // We need to keep track of this count in order to add it back to the capacity when freeing. 58 descriptors: DescriptorsCount, 59 // We keep the parent of the pool alive, otherwise it would be destroyed. 60 pool_parent: Arc<StdDescriptorPool>, 61 } 62 63 unsafe impl DescriptorPool for Arc<StdDescriptorPool> { 64 type Alloc = StdDescriptorPoolAlloc; 65 66 // TODO: eventually use a lock-free algorithm? alloc(&mut self, layout: &DescriptorSetLayout) -> Result<StdDescriptorPoolAlloc, OomError>67 fn alloc(&mut self, layout: &DescriptorSetLayout) -> Result<StdDescriptorPoolAlloc, OomError> { 68 let mut pools = self.pools.lock().unwrap(); 69 70 // Try find an existing pool with some free space. 71 for pool_arc in pools.iter_mut() { 72 let mut pool = pool_arc.lock().unwrap(); 73 74 if pool.remaining_sets_count == 0 { 75 continue; 76 } 77 78 if !(pool.remaining_capacity >= *layout.descriptors_count()) { 79 continue; 80 } 81 82 // Note that we decrease these values *before* trying to allocate from the pool. 83 // If allocating from the pool results in an error, we just ignore it. In order to 84 // avoid trying the same failing pool every time, we "pollute" it by reducing the 85 // available space. 86 pool.remaining_sets_count -= 1; 87 pool.remaining_capacity -= *layout.descriptors_count(); 88 89 let alloc = unsafe { 90 match pool.pool.alloc(Some(layout)) { 91 Ok(mut sets) => sets.next().unwrap(), 92 // An error can happen if we're out of memory, or if the pool is fragmented. 93 // We handle these errors by just ignoring this pool and trying the next ones. 94 Err(_) => continue, 95 } 96 }; 97 98 return Ok(StdDescriptorPoolAlloc { 99 pool: pool_arc.clone(), 100 set: Some(alloc), 101 descriptors: *layout.descriptors_count(), 102 pool_parent: self.clone(), 103 }); 104 } 105 106 // No existing pool can be used. Create a new one. 107 // We use an arbitrary number of 40 sets and 40 times the requested descriptors. 108 let count = layout.descriptors_count().clone() * 40; 109 // Failure to allocate a new pool results in an error for the whole function because 110 // there's no way we can recover from that. 111 let mut new_pool = UnsafeDescriptorPool::new(self.device.clone(), &count, 40, true)?; 112 113 let alloc = unsafe { 114 match new_pool.alloc(Some(layout)) { 115 Ok(mut sets) => sets.next().unwrap(), 116 Err(DescriptorPoolAllocError::OutOfHostMemory) => { 117 return Err(OomError::OutOfHostMemory); 118 } 119 Err(DescriptorPoolAllocError::OutOfDeviceMemory) => { 120 return Err(OomError::OutOfDeviceMemory); 121 } 122 // A fragmented pool error can't happen at the first ever allocation. 123 Err(DescriptorPoolAllocError::FragmentedPool) => unreachable!(), 124 // Out of pool memory cannot happen at the first ever allocation. 125 Err(DescriptorPoolAllocError::OutOfPoolMemory) => unreachable!(), 126 } 127 }; 128 129 let pool_obj = Arc::new(Mutex::new(Pool { 130 pool: new_pool, 131 remaining_capacity: count - *layout.descriptors_count(), 132 remaining_sets_count: 40 - 1, 133 })); 134 135 pools.push(pool_obj.clone()); 136 137 Ok(StdDescriptorPoolAlloc { 138 pool: pool_obj, 139 set: Some(alloc), 140 descriptors: *layout.descriptors_count(), 141 pool_parent: self.clone(), 142 }) 143 } 144 } 145 146 unsafe impl DeviceOwned for StdDescriptorPool { 147 #[inline] device(&self) -> &Arc<Device>148 fn device(&self) -> &Arc<Device> { 149 &self.device 150 } 151 } 152 153 impl DescriptorPoolAlloc for StdDescriptorPoolAlloc { 154 #[inline] inner(&self) -> &UnsafeDescriptorSet155 fn inner(&self) -> &UnsafeDescriptorSet { 156 self.set.as_ref().unwrap() 157 } 158 159 #[inline] inner_mut(&mut self) -> &mut UnsafeDescriptorSet160 fn inner_mut(&mut self) -> &mut UnsafeDescriptorSet { 161 self.set.as_mut().unwrap() 162 } 163 } 164 165 impl Drop for StdDescriptorPoolAlloc { 166 // This is the destructor of a single allocation (not of the whole pool). drop(&mut self)167 fn drop(&mut self) { 168 unsafe { 169 let mut pool = self.pool.lock().unwrap(); 170 pool.pool.free(self.set.take()).unwrap(); 171 // Add back the capacity only after freeing, in case of a panic during the free. 172 pool.remaining_sets_count += 1; 173 pool.remaining_capacity += self.descriptors; 174 } 175 } 176 } 177 178 #[cfg(test)] 179 mod tests { 180 use crate::descriptor_set::layout::DescriptorDesc; 181 use crate::descriptor_set::layout::DescriptorDescTy; 182 use crate::descriptor_set::layout::DescriptorSetDesc; 183 use crate::descriptor_set::layout::DescriptorSetLayout; 184 use crate::descriptor_set::pool::DescriptorPool; 185 use crate::descriptor_set::pool::StdDescriptorPool; 186 use crate::pipeline::shader::ShaderStages; 187 use std::iter; 188 use std::sync::Arc; 189 190 #[test] desc_pool_kept_alive()191 fn desc_pool_kept_alive() { 192 // Test that the `StdDescriptorPool` is kept alive by its allocations. 193 let (device, _) = gfx_dev_and_queue!(); 194 195 let desc = DescriptorDesc { 196 ty: DescriptorDescTy::Sampler, 197 array_count: 1, 198 stages: ShaderStages::all(), 199 readonly: false, 200 }; 201 let layout = DescriptorSetLayout::new( 202 device.clone(), 203 DescriptorSetDesc::new(iter::once(Some(desc))), 204 ) 205 .unwrap(); 206 207 let mut pool = Arc::new(StdDescriptorPool::new(device)); 208 let pool_weak = Arc::downgrade(&pool); 209 let alloc = pool.alloc(&layout); 210 drop(pool); 211 assert!(pool_weak.upgrade().is_some()); 212 } 213 } 214