1 // Copyright (c) 2017 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 //! Efficiently suballocates buffers into smaller subbuffers.
11 
12 use super::{
13     sys::BufferCreateInfo, Buffer, BufferContents, BufferError, BufferMemory, BufferUsage,
14     Subbuffer,
15 };
16 use crate::{
17     device::{Device, DeviceOwned},
18     memory::{
19         allocator::{
20             align_up, AllocationCreateInfo, AllocationCreationError, DeviceLayout, MemoryAllocator,
21             MemoryUsage, StandardMemoryAllocator,
22         },
23         DeviceAlignment,
24     },
25     DeviceSize, NonZeroDeviceSize,
26 };
27 use crossbeam_queue::ArrayQueue;
28 use std::{
29     cell::UnsafeCell,
30     cmp,
31     hash::{Hash, Hasher},
32     mem::ManuallyDrop,
33     sync::Arc,
34 };
35 
36 const MAX_ARENAS: usize = 32;
37 
38 /// Efficiently suballocates buffers into smaller subbuffers.
39 ///
40 /// This allocator is especially suitable when you want to upload or download some data regularly
41 /// (for example, at each frame for a video game).
42 ///
43 /// # Algorithm
44 ///
45 /// The allocator keeps a pool of *arenas*. An arena is simply a buffer in which *arena allocation*
46 /// takes place, also known as *bump allocation* or *linear allocation*. Every time you allocate,
47 /// one of these arenas is suballocated. If there is no arena that is currently available, one will
48 /// be allocated. After all subbuffers allocated from an arena are dropped, the arena is
49 /// automatically returned to the arena pool. If you try to allocate a subbuffer larger than the
50 /// current size of an arena, the arenas are automatically resized.
51 ///
52 /// No memory is allocated when the allocator is created, be it on the Vulkan or Rust side. That
53 /// only happens once you allocate a subbuffer.
54 ///
55 /// # Usage
56 ///
57 /// Ideally, one arena should be able to fit all data you need to update per frame, so that each
58 /// arena is submitted and freed once per frame. This way, the arena pool would also contain as
59 /// many arenas as there are frames in flight on the thread. Otherwise, if your arenas are not able
60 /// to fit everything each frame, what will likely happen is that each subbuffer will be
61 /// allocated from an individual arena. This can impact efficiency both in terms of memory usage
62 /// (because each arena has the same size, even if some of the subbuffers are way smaller) as well
63 /// as performance, because the data could end up more physically separated in memory, which means
64 /// the GPU would need to hop from place to place a lot more during a frame.
65 ///
66 /// Ideally the result is something roughly like this:
67 ///
68 /// ```plain
69 /// +---------------------------------------------------------------------------------------------+
70 /// |                                        Memory Block                                         |
71 /// |-----+------+-----------------------+---------+-----------------------+------+---------+-----|
72 /// |     |      |     Frame 1 Arena     |         |     Frame 2 Arena     |      |         |     |
73 /// | ••• | Tex. |-------+-------+-------| Attach. |-------+-------+-------| Tex. | Attach. | ••• |
74 /// |     |      | Vert. | Indx. | Unif. |         | Vert. | Indx. | Unif. |      |         |     |
75 /// +-----+------+-------+-------+-------+---------+-------+-------+-------+------+---------+-----+
76 /// ```
77 ///
78 /// Download or device-only usage is much the same. Try to make the arenas fit all the data you
79 /// need to store at once.
80 ///
81 /// # Examples
82 ///
83 /// ```
84 /// use vulkano::buffer::allocator::SubbufferAllocator;
85 /// use vulkano::command_buffer::{
86 ///     AutoCommandBufferBuilder, CommandBufferUsage, PrimaryCommandBufferAbstract,
87 /// };
88 /// use vulkano::sync::GpuFuture;
89 /// # let queue: std::sync::Arc<vulkano::device::Queue> = return;
90 /// # let memory_allocator: std::sync::Arc<vulkano::memory::allocator::StandardMemoryAllocator> = return;
91 /// # let command_buffer_allocator: vulkano::command_buffer::allocator::StandardCommandBufferAllocator = return;
92 ///
93 /// // Create the buffer allocator.
94 /// let buffer_allocator = SubbufferAllocator::new(memory_allocator.clone(), Default::default());
95 ///
96 /// for n in 0..25u32 {
97 ///     // Each loop allocates a new subbuffer and stores `data` in it.
98 ///     let data: [f32; 4] = [1.0, 0.5, n as f32 / 24.0, 0.0];
99 ///     let subbuffer = buffer_allocator.allocate_sized().unwrap();
100 ///     *subbuffer.write().unwrap() = data;
101 ///
102 ///     // You can then use `subbuffer` as if it was an entirely separate buffer.
103 ///     AutoCommandBufferBuilder::primary(
104 ///         &command_buffer_allocator,
105 ///         queue.queue_family_index(),
106 ///         CommandBufferUsage::OneTimeSubmit,
107 ///     )
108 ///     .unwrap()
109 ///     // For the sake of the example we just call `update_buffer` on the buffer, even though
110 ///     // it is pointless to do that.
111 ///     .update_buffer(subbuffer.clone(), &[0.2, 0.3, 0.4, 0.5])
112 ///     .unwrap()
113 ///     .build().unwrap()
114 ///     .execute(queue.clone())
115 ///     .unwrap()
116 ///     .then_signal_fence_and_flush()
117 ///     .unwrap();
118 /// }
119 /// ```
120 #[derive(Debug)]
121 pub struct SubbufferAllocator<A = Arc<StandardMemoryAllocator>> {
122     state: UnsafeCell<SubbufferAllocatorState<A>>,
123 }
124 
125 impl<A> SubbufferAllocator<A>
126 where
127     A: MemoryAllocator,
128 {
129     /// Creates a new `SubbufferAllocator`.
new(memory_allocator: A, create_info: SubbufferAllocatorCreateInfo) -> Self130     pub fn new(memory_allocator: A, create_info: SubbufferAllocatorCreateInfo) -> Self {
131         let SubbufferAllocatorCreateInfo {
132             arena_size,
133             buffer_usage,
134             memory_usage,
135             _ne: _,
136         } = create_info;
137 
138         let properties = memory_allocator.device().physical_device().properties();
139         let buffer_alignment = [
140             buffer_usage
141                 .intersects(BufferUsage::UNIFORM_TEXEL_BUFFER | BufferUsage::STORAGE_TEXEL_BUFFER)
142                 .then_some(properties.min_texel_buffer_offset_alignment),
143             buffer_usage
144                 .contains(BufferUsage::UNIFORM_BUFFER)
145                 .then_some(properties.min_uniform_buffer_offset_alignment),
146             buffer_usage
147                 .contains(BufferUsage::STORAGE_BUFFER)
148                 .then_some(properties.min_storage_buffer_offset_alignment),
149         ]
150         .into_iter()
151         .flatten()
152         .max()
153         .unwrap_or(DeviceAlignment::MIN);
154 
155         SubbufferAllocator {
156             state: UnsafeCell::new(SubbufferAllocatorState {
157                 memory_allocator,
158                 buffer_usage,
159                 memory_usage,
160                 buffer_alignment,
161                 arena_size,
162                 arena: None,
163                 free_start: 0,
164                 reserve: None,
165             }),
166         }
167     }
168 
169     /// Returns the current size of the arenas.
arena_size(&self) -> DeviceSize170     pub fn arena_size(&self) -> DeviceSize {
171         unsafe { &*self.state.get() }.arena_size
172     }
173 
174     /// Sets the arena size to the provided `size`.
175     ///
176     /// The next time you allocate a subbuffer, a new arena will be allocated with the new size,
177     /// and all subsequently allocated arenas will also share the new size.
set_arena_size(&self, size: DeviceSize)178     pub fn set_arena_size(&self, size: DeviceSize) {
179         let state = unsafe { &mut *self.state.get() };
180         state.arena_size = size;
181         state.arena = None;
182         state.reserve = None;
183     }
184 
185     /// Ensures that the size of the current arena is at least `size`.
186     ///
187     /// If `size` is greater than the current arena size, then a new arena will be allocated with
188     /// the new size, and all subsequently allocated arenas will also share the new size. Otherwise
189     /// this has no effect.
reserve(&self, size: DeviceSize) -> Result<(), AllocationCreationError>190     pub fn reserve(&self, size: DeviceSize) -> Result<(), AllocationCreationError> {
191         if size > self.arena_size() {
192             let state = unsafe { &mut *self.state.get() };
193             state.arena_size = size;
194             state.reserve = None;
195             state.arena = Some(state.next_arena()?);
196         }
197 
198         Ok(())
199     }
200 
201     /// Allocates a subbuffer for sized data.
allocate_sized<T>(&self) -> Result<Subbuffer<T>, AllocationCreationError> where T: BufferContents,202     pub fn allocate_sized<T>(&self) -> Result<Subbuffer<T>, AllocationCreationError>
203     where
204         T: BufferContents,
205     {
206         let layout = T::LAYOUT.unwrap_sized();
207 
208         unsafe { &mut *self.state.get() }
209             .allocate(layout)
210             .map(|subbuffer| unsafe { subbuffer.reinterpret_unchecked() })
211     }
212 
213     /// Allocates a subbuffer for a slice.
214     ///
215     /// # Panics
216     ///
217     /// - Panics if `len` is zero.
allocate_slice<T>( &self, len: DeviceSize, ) -> Result<Subbuffer<[T]>, AllocationCreationError> where T: BufferContents,218     pub fn allocate_slice<T>(
219         &self,
220         len: DeviceSize,
221     ) -> Result<Subbuffer<[T]>, AllocationCreationError>
222     where
223         T: BufferContents,
224     {
225         self.allocate_unsized(len)
226     }
227 
228     /// Allocates a subbuffer for unsized data.
229     ///
230     /// # Panics
231     ///
232     /// - Panics if `len` is zero.
allocate_unsized<T>( &self, len: DeviceSize, ) -> Result<Subbuffer<T>, AllocationCreationError> where T: BufferContents + ?Sized,233     pub fn allocate_unsized<T>(
234         &self,
235         len: DeviceSize,
236     ) -> Result<Subbuffer<T>, AllocationCreationError>
237     where
238         T: BufferContents + ?Sized,
239     {
240         let len = NonZeroDeviceSize::new(len).expect("empty slices are not valid buffer contents");
241         let layout = T::LAYOUT.layout_for_len(len).unwrap();
242 
243         unsafe { &mut *self.state.get() }
244             .allocate(layout)
245             .map(|subbuffer| unsafe { subbuffer.reinterpret_unchecked() })
246     }
247 
248     /// Allocates a subbuffer with the given `layout`.
249     ///
250     /// # Panics
251     ///
252     /// - Panics if `layout.alignment()` exceeds `64`.
allocate( &self, layout: DeviceLayout, ) -> Result<Subbuffer<[u8]>, AllocationCreationError>253     pub fn allocate(
254         &self,
255         layout: DeviceLayout,
256     ) -> Result<Subbuffer<[u8]>, AllocationCreationError> {
257         assert!(layout.alignment().as_devicesize() <= 64);
258 
259         unsafe { &mut *self.state.get() }.allocate(layout)
260     }
261 }
262 
263 unsafe impl<A> DeviceOwned for SubbufferAllocator<A>
264 where
265     A: MemoryAllocator,
266 {
device(&self) -> &Arc<Device>267     fn device(&self) -> &Arc<Device> {
268         unsafe { &*self.state.get() }.memory_allocator.device()
269     }
270 }
271 
272 #[derive(Debug)]
273 struct SubbufferAllocatorState<A> {
274     memory_allocator: A,
275     buffer_usage: BufferUsage,
276     memory_usage: MemoryUsage,
277     // The alignment required for the subbuffers.
278     buffer_alignment: DeviceAlignment,
279     // The current size of the arenas.
280     arena_size: DeviceSize,
281     // Contains the buffer that is currently being suballocated.
282     arena: Option<Arc<Arena>>,
283     // Offset pointing to the start of free memory within the arena.
284     free_start: DeviceSize,
285     // When an `Arena` is dropped, it returns itself here for reuse.
286     reserve: Option<Arc<ArrayQueue<Arc<Buffer>>>>,
287 }
288 
289 impl<A> SubbufferAllocatorState<A>
290 where
291     A: MemoryAllocator,
292 {
allocate( &mut self, layout: DeviceLayout, ) -> Result<Subbuffer<[u8]>, AllocationCreationError>293     fn allocate(
294         &mut self,
295         layout: DeviceLayout,
296     ) -> Result<Subbuffer<[u8]>, AllocationCreationError> {
297         let size = layout.size();
298         let alignment = cmp::max(layout.alignment(), self.buffer_alignment);
299 
300         loop {
301             if self.arena.is_none() {
302                 // If the requested size is larger than the arenas, we need to resize them.
303                 if self.arena_size < size {
304                     self.arena_size = size * 2;
305                     // We need to drop our reference to the old pool to make sure the arenas are
306                     // dropped once no longer in use, and replace it with a new pool that will not
307                     // be polluted with the outdates arenas.
308                     self.reserve = None;
309                 }
310                 self.arena = Some(self.next_arena()?);
311                 self.free_start = 0;
312             }
313 
314             let arena = self.arena.as_ref().unwrap();
315             let allocation = match arena.buffer.memory() {
316                 BufferMemory::Normal(a) => a,
317                 BufferMemory::Sparse => unreachable!(),
318             };
319             let arena_offset = allocation.offset();
320             let atom_size = allocation.atom_size().unwrap_or(DeviceAlignment::MIN);
321 
322             let alignment = cmp::max(alignment, atom_size);
323             let offset = align_up(arena_offset + self.free_start, alignment);
324 
325             if offset + size <= arena_offset + self.arena_size {
326                 let offset = offset - arena_offset;
327                 self.free_start = offset + size;
328 
329                 return Ok(Subbuffer::from_arena(arena.clone(), offset, layout.size()));
330             }
331 
332             // We reached the end of the arena, grab the next one.
333             self.arena = None;
334         }
335     }
336 
next_arena(&mut self) -> Result<Arc<Arena>, AllocationCreationError>337     fn next_arena(&mut self) -> Result<Arc<Arena>, AllocationCreationError> {
338         if self.reserve.is_none() {
339             self.reserve = Some(Arc::new(ArrayQueue::new(MAX_ARENAS)));
340         }
341         let reserve = self.reserve.as_ref().unwrap();
342 
343         reserve
344             .pop()
345             .map(Ok)
346             .unwrap_or_else(|| self.create_arena())
347             .map(|buffer| {
348                 Arc::new(Arena {
349                     buffer: ManuallyDrop::new(buffer),
350                     reserve: reserve.clone(),
351                 })
352             })
353     }
354 
create_arena(&self) -> Result<Arc<Buffer>, AllocationCreationError>355     fn create_arena(&self) -> Result<Arc<Buffer>, AllocationCreationError> {
356         Buffer::new(
357             &self.memory_allocator,
358             BufferCreateInfo {
359                 usage: self.buffer_usage,
360                 ..Default::default()
361             },
362             AllocationCreateInfo {
363                 usage: self.memory_usage,
364                 ..Default::default()
365             },
366             DeviceLayout::from_size_alignment(self.arena_size, 1).unwrap(),
367         )
368         .map_err(|err| match err {
369             BufferError::AllocError(err) => err,
370             // We don't use sparse-binding, concurrent sharing or external memory, therefore the
371             // other errors can't happen.
372             _ => unreachable!(),
373         })
374     }
375 }
376 
377 #[derive(Debug)]
378 pub(super) struct Arena {
379     buffer: ManuallyDrop<Arc<Buffer>>,
380     // Where we return the arena in our `Drop` impl.
381     reserve: Arc<ArrayQueue<Arc<Buffer>>>,
382 }
383 
384 impl Arena {
buffer(&self) -> &Arc<Buffer>385     pub(super) fn buffer(&self) -> &Arc<Buffer> {
386         &self.buffer
387     }
388 }
389 
390 impl Drop for Arena {
drop(&mut self)391     fn drop(&mut self) {
392         let buffer = unsafe { ManuallyDrop::take(&mut self.buffer) };
393         let _ = self.reserve.push(buffer);
394     }
395 }
396 
397 impl PartialEq for Arena {
eq(&self, other: &Self) -> bool398     fn eq(&self, other: &Self) -> bool {
399         self.buffer == other.buffer
400     }
401 }
402 
403 impl Eq for Arena {}
404 
405 impl Hash for Arena {
hash<H: Hasher>(&self, state: &mut H)406     fn hash<H: Hasher>(&self, state: &mut H) {
407         self.buffer.hash(state);
408     }
409 }
410 
411 /// Parameters to create a new [`SubbufferAllocator`].
412 pub struct SubbufferAllocatorCreateInfo {
413     /// Initial size of an arena in bytes.
414     ///
415     /// Ideally this should fit all the data you need to update per frame. So for example, if you
416     /// need to allocate buffers of size 1K, 2K and 5K each frame, then this should be 8K. If your
417     /// data is dynamically-sized then try to make an educated guess or simply leave the default.
418     ///
419     /// The default value is `0`.
420     pub arena_size: DeviceSize,
421 
422     /// The buffer usage that all allocated buffers should have.
423     ///
424     /// The default value is [`BufferUsage::TRANSFER_SRC`].
425     pub buffer_usage: BufferUsage,
426 
427     /// The memory usage that all buffers should be allocated with.
428     ///
429     /// The default value is [`MemoryUsage::Upload`].
430     pub memory_usage: MemoryUsage,
431 
432     pub _ne: crate::NonExhaustive,
433 }
434 
435 impl Default for SubbufferAllocatorCreateInfo {
436     #[inline]
default() -> Self437     fn default() -> Self {
438         SubbufferAllocatorCreateInfo {
439             arena_size: 0,
440             buffer_usage: BufferUsage::TRANSFER_SRC,
441             memory_usage: MemoryUsage::Upload,
442             _ne: crate::NonExhaustive(()),
443         }
444     }
445 }
446 
447 #[cfg(test)]
448 mod tests {
449     use super::*;
450 
451     #[test]
reserve()452     fn reserve() {
453         let (device, _) = gfx_dev_and_queue!();
454         let memory_allocator = StandardMemoryAllocator::new_default(device);
455 
456         let buffer_allocator = SubbufferAllocator::new(memory_allocator, Default::default());
457         assert_eq!(buffer_allocator.arena_size(), 0);
458 
459         buffer_allocator.reserve(83).unwrap();
460         assert_eq!(buffer_allocator.arena_size(), 83);
461     }
462 
463     #[test]
capacity_increase()464     fn capacity_increase() {
465         let (device, _) = gfx_dev_and_queue!();
466         let memory_allocator = StandardMemoryAllocator::new_default(device);
467 
468         let buffer_allocator = SubbufferAllocator::new(memory_allocator, Default::default());
469         assert_eq!(buffer_allocator.arena_size(), 0);
470 
471         buffer_allocator.allocate_sized::<u32>().unwrap();
472         assert_eq!(buffer_allocator.arena_size(), 8);
473     }
474 }
475