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