• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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