3 align_up, error::AllocationError, heap::Heap, slab::Slab, unreachable_unchecked,
\r
4 util::try_arc_unwrap, MemoryBounds,
\r
6 alloc::{sync::Arc, vec::Vec},
\r
7 core::{convert::TryFrom as _, mem::replace, ptr::NonNull},
\r
8 gpu_alloc_types::{AllocationFlags, DeviceMapError, MemoryDevice, MemoryPropertyFlags},
\r
12 pub(crate) struct BuddyBlock<M> {
\r
14 pub ptr: Option<NonNull<u8>>,
\r
21 unsafe impl<M> Sync for BuddyBlock<M> where M: Sync {}
\r
22 unsafe impl<M> Send for BuddyBlock<M> where M: Send {}
\r
24 #[derive(Clone, Copy, Debug)]
\r
35 unsafe fn replace_next(&mut self, value: usize) -> usize {
\r
37 PairState::Exhausted => unreachable_unchecked(),
\r
38 PairState::Ready { next, .. } => replace(next, value),
\r
42 unsafe fn replace_prev(&mut self, value: usize) -> usize {
\r
44 PairState::Exhausted => unreachable_unchecked(),
\r
45 PairState::Ready { prev, .. } => replace(prev, value),
\r
50 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
\r
62 parent: Option<usize>,
\r
65 struct SizeBlockEntry {
\r
74 pairs: Slab<PairEntry>,
\r
91 unsafe fn add_pair_and_acquire_left(
\r
95 parent: Option<usize>,
\r
96 ) -> SizeBlockEntry {
\r
97 if self.next_ready < self.pairs.len() {
\r
98 unreachable_unchecked()
\r
101 let index = self.pairs.insert(PairEntry {
\r
102 state: PairState::Exhausted,
\r
108 let entry = self.pairs.get_unchecked_mut(index);
\r
109 entry.state = PairState::Ready {
\r
112 ready: Right, // Left is allocated.
\r
114 self.next_ready = index;
\r
123 fn acquire(&mut self, size: u64) -> Option<SizeBlockEntry> {
\r
124 if self.next_ready >= self.pairs.len() {
\r
128 let ready = self.next_ready;
\r
130 let entry = unsafe { self.pairs.get_unchecked_mut(ready) };
\r
131 let chunk = entry.chunk;
\r
132 let offset = entry.offset;
\r
134 let bit = match entry.state {
\r
135 PairState::Exhausted => unsafe { unreachable_unchecked() },
\r
136 PairState::Ready { ready, next, prev } => {
\r
137 entry.state = PairState::Exhausted;
\r
139 if prev == self.next_ready {
\r
140 // The only ready entry.
\r
141 debug_assert_eq!(next, self.next_ready);
\r
142 self.next_ready = self.pairs.len();
\r
144 let prev_entry = unsafe { self.pairs.get_unchecked_mut(prev) };
\r
145 let prev_next = unsafe { prev_entry.state.replace_next(next) };
\r
146 debug_assert_eq!(prev_next, self.next_ready);
\r
148 let next_entry = unsafe { self.pairs.get_unchecked_mut(next) };
\r
149 let next_prev = unsafe { next_entry.state.replace_prev(prev) };
\r
150 debug_assert_eq!(next_prev, self.next_ready);
\r
152 self.next_ready = next;
\r
162 Some(SizeBlockEntry {
\r
164 offset: offset + bit as u64 * size,
\r
165 index: (ready << 1) | bit as usize,
\r
169 fn release(&mut self, index: usize) -> Release {
\r
170 let side = match index & 1 {
\r
173 _ => unsafe { unreachable_unchecked() },
\r
175 let entry_index = index >> 1;
\r
177 let len = self.pairs.len();
\r
179 let entry = self.pairs.get_mut(entry_index);
\r
181 let chunk = entry.chunk;
\r
182 let offset = entry.offset;
\r
183 let parent = entry.parent;
\r
185 match entry.state {
\r
186 PairState::Exhausted => {
\r
187 if self.next_ready == len {
\r
188 entry.state = PairState::Ready {
\r
193 self.next_ready = entry_index;
\r
195 debug_assert!(self.next_ready < len);
\r
197 let next = self.next_ready;
\r
198 let next_entry = unsafe { self.pairs.get_unchecked_mut(next) };
\r
199 let prev = unsafe { next_entry.state.replace_prev(entry_index) };
\r
201 let prev_entry = unsafe { self.pairs.get_unchecked_mut(prev) };
\r
202 let prev_next = unsafe { prev_entry.state.replace_next(entry_index) };
\r
203 debug_assert_eq!(prev_next, next);
\r
205 let entry = unsafe { self.pairs.get_unchecked_mut(entry_index) };
\r
206 entry.state = PairState::Ready {
\r
215 PairState::Ready { ready, .. } if ready == side => {
\r
216 panic!("Attempt to dealloate already free block")
\r
219 PairState::Ready { next, prev, .. } => {
\r
221 self.pairs.remove_unchecked(entry_index);
\r
224 if prev == entry_index {
\r
225 debug_assert_eq!(next, entry_index);
\r
226 self.next_ready = self.pairs.len();
\r
228 let prev_entry = unsafe { self.pairs.get_unchecked_mut(prev) };
\r
229 let prev_next = unsafe { prev_entry.state.replace_next(next) };
\r
230 debug_assert_eq!(prev_next, entry_index);
\r
232 let next_entry = unsafe { self.pairs.get_unchecked_mut(next) };
\r
233 let next_prev = unsafe { next_entry.state.replace_prev(prev) };
\r
234 debug_assert_eq!(next_prev, entry_index);
\r
236 self.next_ready = next;
\r
240 Some(parent) => Release::Parent(parent),
\r
242 debug_assert_eq!(offset, 0);
\r
243 Release::Chunk(chunk)
\r
254 ptr: Option<NonNull<u8>>,
\r
259 pub(crate) struct BuddyAllocator<M> {
\r
261 chunks: Slab<Chunk<M>>,
\r
264 props: MemoryPropertyFlags,
\r
268 unsafe impl<M> Sync for BuddyAllocator<M> where M: Sync {}
\r
269 unsafe impl<M> Send for BuddyAllocator<M> where M: Send {}
\r
271 impl<M> BuddyAllocator<M>
\r
273 M: MemoryBounds + 'static,
\r
277 initial_dedicated_size: u64,
\r
279 props: MemoryPropertyFlags,
\r
283 minimal_size.is_power_of_two(),
\r
284 "Minimal allocation size of buddy allocator must be power of two"
\r
287 initial_dedicated_size.is_power_of_two(),
\r
288 "Dedicated allocation size of buddy allocator must be power of two"
\r
291 let initial_sizes = (initial_dedicated_size
\r
293 .saturating_sub(minimal_size.trailing_zeros())) as usize;
\r
297 chunks: Slab::new(),
\r
298 sizes: (0..initial_sizes).map(|_| Size::new()).collect(),
\r
301 atom_mask: atom_mask | (minimal_size - 1),
\r
305 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, device)))]
\r
306 pub unsafe fn alloc(
\r
308 device: &impl MemoryDevice<M>,
\r
311 flags: AllocationFlags,
\r
313 allocations_remains: &mut u32,
\r
314 ) -> Result<BuddyBlock<M>, AllocationError> {
\r
315 let align_mask = align_mask | self.atom_mask;
\r
317 let size = align_up(size, align_mask)
\r
318 .and_then(|size| size.checked_next_power_of_two())
\r
319 .ok_or(AllocationError::OutOfDeviceMemory)?;
\r
321 let size = size.max(self.minimal_size);
\r
323 let size_index = size.trailing_zeros() - self.minimal_size.trailing_zeros();
\r
325 usize::try_from(size_index).map_err(|_| AllocationError::OutOfDeviceMemory)?;
\r
327 while self.sizes.len() <= size_index {
\r
328 self.sizes.push(Size::new());
\r
331 let host_visible = self.host_visible();
\r
333 let mut candidate_size_index = size_index;
\r
335 let (mut entry, entry_size_index) = loop {
\r
336 let sizes_len = self.sizes.len();
\r
338 let candidate_size_entry = &mut self.sizes[candidate_size_index];
\r
339 let candidate_size = self.minimal_size << candidate_size_index;
\r
341 if let Some(entry) = candidate_size_entry.acquire(candidate_size) {
\r
342 break (entry, candidate_size_index);
\r
345 if sizes_len == candidate_size_index + 1 {
\r
346 // That's size of device allocation.
\r
347 if *allocations_remains == 0 {
\r
348 return Err(AllocationError::TooManyObjects);
\r
351 let chunk_size = self.minimal_size << (candidate_size_index + 1);
\r
352 let mut memory = device.allocate_memory(chunk_size, self.memory_type, flags)?;
\r
353 *allocations_remains -= 1;
\r
354 heap.alloc(chunk_size);
\r
356 let ptr = if host_visible {
\r
357 match device.map_memory(&mut memory, 0, chunk_size) {
\r
358 Ok(ptr) => Some(ptr),
\r
359 Err(DeviceMapError::OutOfDeviceMemory) => {
\r
360 return Err(AllocationError::OutOfDeviceMemory)
\r
362 Err(DeviceMapError::MapFailed) | Err(DeviceMapError::OutOfHostMemory) => {
\r
363 return Err(AllocationError::OutOfHostMemory)
\r
370 let chunk = self.chunks.insert(Chunk {
\r
371 memory: Arc::new(memory),
\r
376 let entry = candidate_size_entry.add_pair_and_acquire_left(chunk, 0, None);
\r
378 break (entry, candidate_size_index);
\r
381 candidate_size_index += 1;
\r
384 for size_index in (size_index..entry_size_index).rev() {
\r
385 let size_entry = &mut self.sizes[size_index];
\r
387 size_entry.add_pair_and_acquire_left(entry.chunk, entry.offset, Some(entry.index));
\r
390 let chunk_entry = self.chunks.get_unchecked(entry.chunk);
\r
396 .map_or(false, |end| end <= chunk_entry.size),
\r
397 "Offset + size is not in chunk bounds"
\r
401 memory: chunk_entry.memory.clone(),
\r
404 .map(|ptr| NonNull::new_unchecked(ptr.as_ptr().add(entry.offset as usize))),
\r
405 offset: entry.offset,
\r
407 chunk: entry.chunk,
\r
408 index: entry.index,
\r
412 #[cfg_attr(feature = "tracing", tracing::instrument(skip(self, device)))]
\r
413 pub unsafe fn dealloc(
\r
415 device: &impl MemoryDevice<M>,
\r
416 block: BuddyBlock<M>,
\r
418 allocations_remains: &mut u32,
\r
420 debug_assert!(block.size.is_power_of_two());
\r
423 (block.size.trailing_zeros() - self.minimal_size.trailing_zeros()) as usize;
\r
425 let mut release_index = block.index;
\r
426 let mut release_size_index = size_index;
\r
429 match self.sizes[release_size_index].release(release_index) {
\r
430 Release::Parent(parent) => {
\r
431 release_size_index += 1;
\r
432 release_index = parent;
\r
434 Release::Chunk(chunk) => {
\r
435 debug_assert_eq!(chunk, block.chunk);
\r
437 self.chunks.get(chunk).size,
\r
438 self.minimal_size << (release_size_index + 1)
\r
440 let chunk = self.chunks.remove(chunk);
\r
443 let memory = try_arc_unwrap(chunk.memory)
\r
444 .expect("Memory shared after last block deallocated");
\r
446 device.deallocate_memory(memory);
\r
447 *allocations_remains += 1;
\r
448 heap.dealloc(chunk.size);
\r
452 Release::None => return,
\r
457 fn host_visible(&self) -> bool {
\r
458 self.props.contains(MemoryPropertyFlags::HOST_VISIBLE)
\r