1use super::*;
9
10#[path = "defragmentation/extensive.rs"]
11mod extensive;
12use extensive::ExtensiveState;
13
14#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
16pub enum DefragmentationAlgorithm {
17 Fast,
19 #[default]
21 Balanced,
22 Full,
24 Extensive,
27}
28
29#[derive(Clone, Copy, Debug)]
31pub struct DefragmentationInfo<'pool> {
32 pub pool: Option<&'pool Pool>,
33 pub max_bytes: u64,
34 pub max_allocations: usize,
35 pub algorithm: DefragmentationAlgorithm,
36}
37
38impl Default for DefragmentationInfo<'_> {
39 fn default() -> Self {
40 Self {
41 pool: None,
42 max_bytes: u64::MAX,
43 max_allocations: usize::MAX,
44 algorithm: DefragmentationAlgorithm::default(),
45 }
46 }
47}
48
49impl<'pool> DefragmentationInfo<'pool> {
50 #[inline]
52 #[must_use]
53 pub fn new() -> Self {
54 Self::default()
55 }
56
57 #[inline]
59 #[must_use]
60 pub fn set_pool(mut self, value: Option<&'pool Pool>) -> Self {
61 self.pool = value;
62 self
63 }
64
65 #[inline]
67 #[must_use]
68 pub fn set_max_bytes(mut self, value: u64) -> Self {
69 self.max_bytes = value;
70 self
71 }
72
73 #[inline]
75 #[must_use]
76 pub fn set_max_allocations(mut self, value: usize) -> Self {
77 self.max_allocations = value;
78 self
79 }
80
81 #[inline]
83 #[must_use]
84 pub fn set_algorithm(mut self, value: DefragmentationAlgorithm) -> Self {
85 self.algorithm = value;
86 self
87 }
88}
89
90#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92pub enum DefragmentationResource {
93 Buffer(vk::Buffer),
94 Image(vk::Image),
95 Unassociated,
98}
99
100#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub struct DefragmentationMove {
106 pub source: AllocationInfo,
107 pub destination: AllocationInfo,
108 pub resource: DefragmentationResource,
109 pub can_alias: bool,
112}
113
114impl DefragmentationMove {
115 #[inline]
117 #[must_use]
118 pub fn set_source(mut self, value: AllocationInfo) -> Self {
119 self.source = value;
120 self
121 }
122
123 #[inline]
125 #[must_use]
126 pub fn set_destination(mut self, value: AllocationInfo) -> Self {
127 self.destination = value;
128 self
129 }
130
131 #[inline]
133 #[must_use]
134 pub fn set_resource(mut self, value: DefragmentationResource) -> Self {
135 self.resource = value;
136 self
137 }
138
139 #[inline]
141 #[must_use]
142 pub fn set_can_alias(mut self, value: bool) -> Self {
143 self.can_alias = value;
144 self
145 }
146}
147
148#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
150pub struct DefragmentationStats {
151 pub moved_allocations: usize,
152 pub moved_bytes: u64,
153 pub freed_blocks: usize,
154 pub freed_bytes: u64,
155 pub ignored_allocations: usize,
156 pub destroyed_allocations: usize,
157}
158
159impl DefragmentationStats {
160 #[inline]
162 #[must_use]
163 pub fn new() -> Self {
164 Self::default()
165 }
166
167 #[inline]
169 #[must_use]
170 pub fn set_moved_allocations(mut self, value: usize) -> Self {
171 self.moved_allocations = value;
172 self
173 }
174
175 #[inline]
177 #[must_use]
178 pub fn set_moved_bytes(mut self, value: u64) -> Self {
179 self.moved_bytes = value;
180 self
181 }
182
183 #[inline]
185 #[must_use]
186 pub fn set_freed_blocks(mut self, value: usize) -> Self {
187 self.freed_blocks = value;
188 self
189 }
190
191 #[inline]
193 #[must_use]
194 pub fn set_freed_bytes(mut self, value: u64) -> Self {
195 self.freed_bytes = value;
196 self
197 }
198
199 #[inline]
201 #[must_use]
202 pub fn set_ignored_allocations(mut self, value: usize) -> Self {
203 self.ignored_allocations = value;
204 self
205 }
206
207 #[inline]
209 #[must_use]
210 pub fn set_destroyed_allocations(mut self, value: usize) -> Self {
211 self.destroyed_allocations = value;
212 self
213 }
214}
215
216struct Move {
217 source_serial: u64,
218 proposal: DefragmentationMove,
219 destination: Option<Allocation>,
220}
221
222pub struct DefragmentationPass<'allocator, 'device> {
232 allocator: &'allocator mut Allocator<'device>,
233 moves: HostVec<'device, Move>,
234 stats: DefragmentationStats,
235 finished: bool,
236 interrupted: bool,
237 context: Option<&'allocator mut ContextState<'device>>,
238}
239
240pub unsafe fn begin_defragmentation<'allocator, 'device>(
258 allocator: &'allocator mut Allocator<'device>,
259 info: &DefragmentationInfo,
260) -> Result<DefragmentationPass<'allocator, 'device>, AllocError> {
261 unsafe { begin_defragmentation_with_break(allocator, info, &mut || false) }
263}
264
265pub unsafe fn begin_defragmentation_with_break<'allocator, 'device>(
275 allocator: &'allocator mut Allocator<'device>,
276 info: &DefragmentationInfo,
277 should_break: &mut dyn FnMut() -> bool,
278) -> Result<DefragmentationPass<'allocator, 'device>, AllocError> {
279 allocator.ensure_global()?;
280 if info.algorithm == DefragmentationAlgorithm::Extensive {
281 return Err(AllocError::UnsupportedCapability(
282 "Extensive relocation requires DefragmentationContext",
283 ));
284 }
285 let mut state = PlanningState::new(allocator, info)?;
287 unsafe { plan(allocator, info, &[], &mut state, should_break) }
289}
290
291#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
294struct Domain {
295 memory_type_index: u32,
296 pool: Option<u64>,
297 automatic: bool,
298 priority: u32,
299 block_size: u64,
300}
301
302fn domain(block: &Block) -> Domain {
303 Domain {
304 memory_type_index: block.memory_type_index,
305 pool: block.pool,
306 automatic: block.automatic,
307 priority: block.priority.get().to_bits(),
308 block_size: if block.automatic { 0 } else { block.size },
309 }
310}
311
312#[derive(Clone, Copy)]
313struct OrderedBlock {
314 handle: vk::DeviceMemory,
315 domain: Domain,
316 free_bytes: u64,
317 rank: usize,
318 immovable: bool,
319}
320
321#[derive(Clone, Copy, Default, PartialEq, Eq)]
322struct BalancedState {
323 average_allocation: u64,
324 average_free: u64,
325}
326
327#[derive(Clone, Copy)]
328struct DomainState {
329 key: Domain,
330 balanced: BalancedState,
331 extensive: ExtensiveState,
332}
333
334struct PlanningState<'device> {
335 blocks: HostVec<'device, OrderedBlock>,
336 domains: HostVec<'device, DomainState>,
337}
338
339impl<'device> PlanningState<'device> {
340 fn new(allocator: &Allocator<'device>, info: &DefragmentationInfo) -> Result<Self, AllocError> {
341 allocator.ensure_open()?;
342 if let Some(pool) = info.pool
343 && pool::validate(allocator, pool)?.info.algorithm != AllocationAlgorithm::Tlsf
344 {
345 return Err(AllocError::UnsupportedCapability(
346 "linear pools cannot defragment",
347 ));
348 }
349 let mut blocks = HostVec::new(allocator.host);
350 blocks
351 .try_reserve(allocator.blocks.len())
352 .map_err(|_| AllocError::HostAllocationFailed)?;
353 for (rank, block) in allocator.blocks.iter().enumerate() {
354 if info
355 .pool
356 .is_some_and(|pool| block.pool != Some(pool.serial))
357 || block.export_types.as_raw() != 0
358 || !matches!(block.metadata, Metadata::Tlsf(_))
359 {
360 continue;
361 }
362 blocks.push_reserved(OrderedBlock {
363 handle: block.handle,
364 domain: domain(block),
365 free_bytes: block.metadata.statistics().free_bytes,
366 rank,
367 immovable: false,
368 });
369 }
370 blocks.sort_unstable_by_key(|block| (block.domain, block.free_bytes, block.rank));
373 for (rank, block) in blocks.iter_mut().enumerate() {
374 block.rank = rank;
375 }
376 let mut domains: HostVec<'device, DomainState> = HostVec::new(allocator.host);
377 domains
378 .try_reserve(blocks.len())
379 .map_err(|_| AllocError::HostAllocationFailed)?;
380 for block in &blocks {
381 if domains.last().is_none_or(|state| state.key != block.domain) {
382 domains.push_reserved(DomainState {
383 key: block.domain,
384 balanced: vector_statistics(allocator, block.domain),
385 extensive: ExtensiveState::default(),
386 });
387 }
388 }
389 Ok(Self { blocks, domains })
390 }
391}
392
393fn vector_statistics(allocator: &Allocator<'_>, key: Domain) -> BalancedState {
394 let (mut allocated, mut free, mut allocations, mut regions) = (0_u128, 0_u128, 0_u128, 0_u128);
396 for block in &allocator.blocks {
397 if domain(block) == key {
398 let stats = block.metadata.statistics();
399 allocated += u128::from(stats.allocated_bytes);
400 free += u128::from(stats.free_bytes);
401 allocations += stats.allocation_count as u128;
402 regions += stats.free_region_count as u128;
403 }
404 }
405 BalancedState {
406 average_allocation: allocated.checked_div(allocations).unwrap_or(0) as u64,
407 average_free: free.checked_div(regions).unwrap_or(0) as u64,
408 }
409}
410
411fn worthwhile_same_block(
412 allocator: &Allocator<'_>,
413 source: &Allocation,
414 averages: BalancedState,
415) -> bool {
416 let block = allocator
417 .blocks
418 .iter()
419 .find(|block| block.handle == source.handle)
420 .expect("live source block");
421 let mut previous = 0;
422 let mut next = 0;
423 block.metadata.for_each_free(|offset, size| {
424 if offset + size == source.offset {
425 previous = size;
426 }
427 if offset == source.offset + source.reserved_size {
428 next = size;
429 }
430 });
431 previous >= averages.average_free / 2
432 || next >= averages.average_free / 2
433 || source.size <= averages.average_free
434 || source.size <= averages.average_allocation
435}
436
437fn reserve_destination(
441 allocator: &mut Allocator<'_>,
442 source: &Allocation,
443 target: vk::DeviceMemory,
444) -> Result<Option<Allocation>, AllocError> {
445 let live = allocator.validate(source)?;
446 let alignment = live.alignment;
447 let class = live.class;
448 let pool = live.pool;
449 let can_alias = live.can_alias;
450 let guard_size = live.guard_size;
451 let guard_offset = live.guard_offset;
452 allocator.reserve()?;
453 let block = allocator
454 .blocks
455 .iter_mut()
456 .find(|block| block.handle == target)
457 .expect("snapshot target block");
458 let (placement, offset) = match block.metadata.allocate_resource(
459 source.reserved_size,
460 alignment,
461 super::super::PlacementStrategy::MinOffset,
462 false,
463 class,
464 ) {
465 Ok(placement) => placement,
466 Err(AllocError::OutOfSpace) => return Ok(None),
467 Err(error) => return Err(error),
468 };
469 if target == source.handle && offset >= source.offset {
470 block
471 .metadata
472 .free(placement)
473 .expect("unpublished same-block placement");
474 return Ok(None);
475 }
476 let size = block.size;
477 block.mapping.write().post_alloc();
478 let serial = publish(
479 allocator,
480 Region {
481 handle: target,
482 index: source.memory_type_index,
483 size: source.size,
484 reserved_size: source.reserved_size,
485 offset,
486 alignment,
487 block_size: size,
488 placement: Some(placement),
489 pool: None,
490 class: ResourceClass::Unknown,
491 },
492 );
493 let live = allocator.live.get_mut(&serial).expect("new destination");
494 live.pool = pool;
495 live.class = class;
496 live.can_alias = can_alias;
497 live.guard_size = guard_size;
498 live.guard_offset = guard_offset;
499 Ok(Some(allocator.published(serial)))
500}
501
502#[derive(Clone, Copy)]
503struct Source {
504 rank: usize,
505 offset: u64,
506 serial: u64,
507}
508
509struct PassCounters<'callback> {
510 remaining: u64,
511 oversized: u8,
512 should_break: &'callback mut dyn FnMut() -> bool,
513 interrupted: bool,
514 panic: Option<Box<dyn std::any::Any + Send>>,
515}
516
517const MAX_OVERSIZED_ALLOCATIONS: u8 = 16;
519
520struct Scan<'a> {
521 blocks: &'a [OrderedBlock],
522 sources: &'a [Source],
523 max_allocations: usize,
524}
525
526#[derive(Clone, Copy)]
527enum Within {
528 Never,
529 Full,
530 Balanced(BalancedState),
531}
532
533#[derive(Clone, Copy)]
534enum ResourceGroup {
535 Textures,
536 Buffers,
537 All,
538}
539
540impl ResourceGroup {
541 fn admits(self, class: ResourceClass) -> bool {
542 match self {
543 Self::Textures => class == ResourceClass::OptimalImage,
544 Self::Buffers => matches!(class, ResourceClass::Buffer | ResourceClass::LinearImage),
545 Self::All => true,
546 }
547 }
548}
549
550struct Selection {
551 sources: std::ops::Range<usize>,
552 targets: std::ops::Range<usize>,
553 earlier_only: bool,
554 within: Within,
555 group: ResourceGroup,
556}
557
558impl PassCounters<'_> {
559 fn exhausted(&self, max_allocations: usize, moves: usize) -> bool {
560 self.interrupted
561 || moves == max_allocations
562 || self.remaining == 0
563 || self.oversized == MAX_OVERSIZED_ALLOCATIONS
564 }
565
566 fn poll(&mut self) -> bool {
567 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(&mut self.should_break)) {
568 Ok(false) => false,
569 Ok(true) => {
570 self.interrupted = true;
571 true
572 }
573 Err(payload) => {
574 self.interrupted = true;
575 self.panic = Some(payload);
576 true
577 }
578 }
579 }
580}
581
582unsafe fn prepare_destination(
586 allocator: &mut Allocator<'_>,
587 source: &Allocation,
588 destination: &Allocation,
589) -> Result<(), AllocError> {
590 unsafe { guard::initialize(allocator, destination) }?;
592 let live = allocator.validate(source)?;
593 let map_count = live.map_count;
594 let persistent = live.persistent;
595 let holds = map_count
596 .checked_add(u32::from(persistent))
597 .ok_or(AllocError::InvalidRange)?;
598 if holds != 0 {
599 let device = allocator.device.clone();
600 unsafe {
603 allocator.mapping_mut(destination).acquire_many(
604 device.as_device(),
605 destination.handle,
606 destination.block_size,
607 holds,
608 )
609 }?;
610 let live = allocator
611 .live
612 .get_mut(&destination.serial)
613 .expect("new destination");
614 live.map_count = map_count;
615 live.persistent = persistent;
616 }
617 Ok(())
618}
619
620unsafe fn record_move(
621 allocator: &mut Allocator<'_>,
622 source: &Allocation,
623 mut destination: Allocation,
624 moves: &mut HostVec<'_, Move>,
625 counters: &mut PassCounters,
626) -> Result<(), AllocError> {
627 if let Err(error) = unsafe { prepare_destination(allocator, source, &destination) } {
629 unsafe { rollback_allocation(allocator, &mut destination) }
631 .expect("debug destination rollback");
632 return Err(error);
633 }
634 let live = &allocator.live[&source.serial];
635 let resource = live.resource;
636 let can_alias = live.can_alias;
637 let destination_info =
638 allocation_info(allocator, &destination).expect("new destination identity");
639 let source_info = allocation_info(allocator, source).expect("source unchanged during planning");
640 counters.remaining -= source.size;
641 moves.push_reserved(Move {
642 source_serial: source.serial,
643 destination: Some(destination),
644 proposal: DefragmentationMove {
645 source: source_info,
646 destination: destination_info,
647 resource: match resource {
648 Some(Resource::Buffer(buffer)) => DefragmentationResource::Buffer(buffer),
649 Some(Resource::Image(image)) => DefragmentationResource::Image(image),
650 None => DefragmentationResource::Unassociated,
651 },
652 can_alias,
653 },
654 });
655 Ok(())
656}
657
658unsafe fn plan_selected(
659 allocator: &mut Allocator<'_>,
660 scan: &Scan<'_>,
661 selection: &Selection,
662 moves: &mut HostVec<'_, Move>,
663 counters: &mut PassCounters,
664) -> Result<(), AllocError> {
665 for source in scan
666 .sources
667 .iter()
668 .filter(|source| selection.sources.contains(&source.rank))
669 {
670 if counters.exhausted(scan.max_allocations, moves.len()) {
671 break;
672 }
673 if moves
674 .iter()
675 .any(|movement| movement.source_serial == source.serial)
676 {
677 continue;
678 }
679 if counters.poll() {
680 break;
681 }
682 let token = allocator.token(source.serial)?;
683 if token.size > counters.remaining {
684 counters.oversized += 1;
685 continue;
686 }
687 counters.oversized = 0;
688 if !selection.group.admits(allocator.live[&source.serial].class) {
689 continue;
690 }
691 let mut destination = None;
692 for rank in selection.targets.clone() {
693 let block = &scan.blocks[rank];
694 if block.handle == token.handle
695 || block.domain != scan.blocks[source.rank].domain
696 || selection.earlier_only && rank >= source.rank
697 {
698 continue;
699 }
700 if let Some(allocation) = reserve_destination(allocator, &token, block.handle)? {
701 destination = Some(allocation);
702 break;
703 }
704 }
705 if destination.is_none()
706 && token.offset != 0
707 && match selection.within {
708 Within::Never => false,
709 Within::Full => true,
710 Within::Balanced(averages) => worthwhile_same_block(allocator, &token, averages),
711 } {
712 destination = reserve_destination(allocator, &token, token.handle)?;
713 }
714 if let Some(destination) = destination {
715 unsafe { record_move(allocator, &token, destination, moves, counters) }?;
717 }
718 }
719 Ok(())
720}
721
722unsafe fn plan<'allocator, 'device>(
723 allocator: &'allocator mut Allocator<'device>,
724 info: &DefragmentationInfo,
725 ignored: &[u64],
726 state: &mut PlanningState<'device>,
727 should_break: &mut dyn FnMut() -> bool,
728) -> Result<DefragmentationPass<'allocator, 'device>, AllocError> {
729 allocator.ensure_open()?;
730 let mut blocks = HostVec::new(allocator.host);
731 blocks
732 .try_reserve(state.blocks.len())
733 .map_err(|_| AllocError::HostAllocationFailed)?;
734 for block in &state.blocks {
735 if allocator
736 .blocks
737 .iter()
738 .any(|live| live.handle == block.handle)
739 {
740 blocks.push_reserved(OrderedBlock {
741 immovable: ignored.iter().any(|serial| {
742 allocator
743 .live
744 .get(serial)
745 .is_some_and(|live| live.handle == block.handle)
746 }),
747 ..*block
748 });
749 }
750 }
751 let mut domains = HostVec::new(allocator.host);
752 domains
753 .try_reserve(state.domains.len())
754 .map_err(|_| AllocError::HostAllocationFailed)?;
755 domains.extend_from_slice_reserved(&state.domains);
756 if info.algorithm == DefragmentationAlgorithm::Extensive && allocator.granularity.0 != Some(1) {
759 extensive::exclude_ignored(&mut blocks, &mut domains);
760 } else {
761 blocks.sort_unstable_by_key(|block| (block.domain, !block.immovable, block.rank));
762 }
763 let mut sources = HostVec::new(allocator.host);
764 sources
765 .try_reserve(allocator.live.len())
766 .map_err(|_| AllocError::HostAllocationFailed)?;
767 for (&serial, live) in &allocator.live {
768 if live.plane.is_some() || live.imported || live.pinned || live.placement.is_none() {
769 continue;
770 }
771 if let Some(rank) = blocks
772 .iter()
773 .position(|block| block.handle == live.handle && !block.immovable)
774 {
775 sources.push_reserved(Source {
776 rank,
777 offset: live.offset,
778 serial,
779 });
780 }
781 }
782 sources.sort_unstable_by(|left, right| {
783 right
784 .rank
785 .cmp(&left.rank)
786 .then(left.offset.cmp(&right.offset))
787 .then(left.serial.cmp(&right.serial))
788 });
789 let mut moves = HostVec::new(allocator.host);
790 moves
791 .try_reserve(sources.len().min(info.max_allocations))
792 .map_err(|_| AllocError::HostAllocationFailed)?;
793 let mut counters = PassCounters {
794 remaining: info.max_bytes,
795 oversized: 0,
796 should_break,
797 interrupted: false,
798 panic: None,
799 };
800 let planned = (|| -> Result<(), AllocError> {
801 for domain in &mut domains {
802 if counters.exhausted(info.max_allocations, moves.len()) {
803 break;
804 }
805 let Some(start) = blocks.iter().position(|block| block.domain == domain.key) else {
806 continue;
807 };
808 let end = blocks[start..]
809 .iter()
810 .position(|block| block.domain != domain.key)
811 .map_or(blocks.len(), |length| start + length);
812 let scan = Scan {
813 blocks: &blocks,
814 sources: &sources,
815 max_allocations: info.max_allocations,
816 };
817 if info.algorithm == DefragmentationAlgorithm::Extensive && allocator.granularity.0 != Some(1)
818 {
819 unsafe {
821 extensive::plan(
822 allocator,
823 &scan,
824 start..end,
825 &mut domain.extensive,
826 &mut moves,
827 &mut counters,
828 )
829 }?;
830 continue;
831 }
832 let within = match info.algorithm {
833 DefragmentationAlgorithm::Fast => Within::Never,
834 DefragmentationAlgorithm::Balanced => Within::Balanced(domain.balanced),
835 DefragmentationAlgorithm::Full | DefragmentationAlgorithm::Extensive => Within::Full,
836 };
837 let mut selection = Selection {
838 sources: start..end,
839 targets: start..end,
840 earlier_only: true,
841 within,
842 group: ResourceGroup::All,
843 };
844 let before = moves.len();
845 unsafe { plan_selected(allocator, &scan, &selection, &mut moves, &mut counters) }?;
847 if before == moves.len()
848 && info.algorithm == DefragmentationAlgorithm::Balanced
849 && !counters.exhausted(info.max_allocations, moves.len())
850 {
851 let refreshed = vector_statistics(allocator, domain.key);
852 if refreshed != domain.balanced {
853 domain.balanced = refreshed;
854 selection.within = Within::Balanced(refreshed);
855 unsafe { plan_selected(allocator, &scan, &selection, &mut moves, &mut counters) }?;
857 }
858 }
859 }
860 Ok(())
861 })();
862 if planned.is_err() || counters.panic.is_some() {
863 for movement in &mut moves {
864 unsafe {
866 rollback_allocation(
867 allocator,
868 movement.destination.as_mut().expect("unpublished move"),
869 )
870 }
871 .expect("planning rollback");
872 }
873 if let Some(payload) = counters.panic.take() {
874 std::panic::resume_unwind(payload);
875 }
876 planned?;
877 }
878 state.blocks = blocks;
879 state.domains = domains;
880 Ok(DefragmentationPass {
881 allocator,
882 moves,
883 stats: DefragmentationStats::new(),
884 finished: false,
885 interrupted: counters.interrupted,
886 context: None,
887 })
888}
889
890impl DefragmentationPass<'_, '_> {
891 pub fn len(&self) -> usize {
893 self.moves.len()
894 }
895
896 pub fn is_empty(&self) -> bool {
897 self.moves.is_empty()
898 }
899
900 pub fn was_interrupted(&self) -> bool {
903 self.interrupted
904 }
905
906 pub fn movement(&self, index: usize) -> Result<&DefragmentationMove, AllocError> {
908 Ok(
909 &self
910 .moves
911 .get(index)
912 .ok_or(AllocError::InvalidRange)?
913 .proposal,
914 )
915 }
916
917 pub fn device(&self) -> &vk::Device {
919 self.allocator.device.as_device()
920 }
921
922 pub fn allocation_callbacks(&self) -> Option<&vk::AllocationCallbacks> {
924 self.allocator.callbacks
925 }
926
927 pub unsafe fn bind_buffer(&mut self, index: usize, buffer: vk::Buffer) -> Result<(), AllocError> {
932 if self.finished {
933 return Err(AllocError::Closed);
934 }
935 let movement = self.moves.get(index).ok_or(AllocError::InvalidRange)?;
936 if !matches!(
937 movement.proposal.resource,
938 DefragmentationResource::Buffer(_) | DefragmentationResource::Unassociated
939 ) {
940 return Err(AllocError::WrongResource);
941 }
942 let destination = movement
943 .destination
944 .as_ref()
945 .ok_or(AllocError::UnknownAllocation)?;
946 unsafe { bind_buffer_memory(self.allocator, buffer, destination) }
948 }
949
950 pub unsafe fn bind_image(&mut self, index: usize, image: vk::Image) -> Result<(), AllocError> {
954 if self.finished {
955 return Err(AllocError::Closed);
956 }
957 let movement = self.moves.get(index).ok_or(AllocError::InvalidRange)?;
958 if !matches!(
959 movement.proposal.resource,
960 DefragmentationResource::Image(_) | DefragmentationResource::Unassociated
961 ) {
962 return Err(AllocError::WrongResource);
963 }
964 let destination = movement
965 .destination
966 .as_ref()
967 .ok_or(AllocError::UnknownAllocation)?;
968 unsafe { bind_image_memory(self.allocator, image, destination) }
970 }
971
972 pub unsafe fn create_aliasing_buffer(
980 &mut self,
981 index: usize,
982 local_offset: u64,
983 info: &vk::BufferCreateInfo,
984 ) -> Result<vk::Buffer, AllocError> {
985 let destination = self.destination(index)?;
986 let token = self.allocator.token(destination.serial)?;
987 unsafe { create_aliasing_buffer(self.allocator, &token, local_offset, info) }
989 }
990
991 pub unsafe fn create_aliasing_image(
997 &mut self,
998 index: usize,
999 local_offset: u64,
1000 info: &vk::ImageCreateInfo,
1001 ) -> Result<vk::Image, AllocError> {
1002 let destination = self.destination(index)?;
1003 let token = self.allocator.token(destination.serial)?;
1004 unsafe { create_aliasing_image(self.allocator, &token, local_offset, info) }
1006 }
1007
1008 fn destination(&self, index: usize) -> Result<&Allocation, AllocError> {
1009 if self.finished {
1010 return Err(AllocError::Closed);
1011 }
1012 self
1013 .moves
1014 .get(index)
1015 .ok_or(AllocError::InvalidRange)?
1016 .destination
1017 .as_ref()
1018 .ok_or(AllocError::UnknownAllocation)
1019 }
1020
1021 pub unsafe fn commit_move(
1038 &mut self,
1039 index: usize,
1040 source: &mut Allocation,
1041 ) -> Result<(), AllocError> {
1042 if self.finished {
1043 return Err(AllocError::Closed);
1044 }
1045 let movement = self.moves.get_mut(index).ok_or(AllocError::InvalidRange)?;
1046 if source.serial != movement.source_serial {
1047 return Err(AllocError::WrongResource);
1048 }
1049 let old = self.allocator.validate(source)?;
1050 let destination = movement
1051 .destination
1052 .as_ref()
1053 .ok_or(AllocError::UnknownAllocation)?;
1054 if (old.bound || old.resource.is_some()) && !self.allocator.validate(destination)?.bound {
1055 return Err(AllocError::InvalidRequest("replacement is not bound"));
1056 }
1057 let user_data = old.user_data;
1058 let pinned = old.pinned;
1059 let name = self
1060 .allocator
1061 .live
1062 .get_mut(&source.serial)
1063 .expect("validated source")
1064 .name
1065 .take();
1066 let old_serial = source.serial;
1067 let before = statistics(self.allocator);
1068 if let Err(error) = unsafe { free_region(self.allocator, source, true) } {
1070 self
1071 .allocator
1072 .live
1073 .get_mut(&old_serial)
1074 .expect("retained failed source")
1075 .name = name;
1076 return Err(error);
1077 }
1078 let destination = movement.destination.take().expect("validated destination");
1079 let live = self
1080 .allocator
1081 .live
1082 .get_mut(&destination.serial)
1083 .expect("live destination");
1084 live.name = name;
1085 live.user_data = user_data;
1086 live.pinned = pinned;
1087 *source = destination;
1088 let after = statistics(self.allocator);
1089 self.stats.moved_allocations += 1;
1090 self.stats.moved_bytes += source.size;
1091 self.stats.freed_blocks += before.native_allocation_count - after.native_allocation_count;
1092 self.stats.freed_bytes += before.native_bytes - after.native_bytes;
1093 Ok(())
1094 }
1095
1096 pub unsafe fn ignore_move(&mut self, index: usize) -> Result<(), AllocError> {
1101 if self.finished {
1102 return Err(AllocError::Closed);
1103 }
1104 let movement = self.moves.get_mut(index).ok_or(AllocError::InvalidRange)?;
1105 let destination = movement
1106 .destination
1107 .as_mut()
1108 .ok_or(AllocError::UnknownAllocation)?;
1109 let before = statistics(self.allocator);
1110 unsafe { free_region(self.allocator, destination, true) }?;
1112 let after = statistics(self.allocator);
1113 self.stats.freed_blocks += before.native_allocation_count - after.native_allocation_count;
1114 self.stats.freed_bytes += before.native_bytes - after.native_bytes;
1115 movement.destination = None;
1116 if let Some(context) = self.context.as_mut() {
1117 if !context.ignored.contains(&movement.source_serial) {
1119 context.ignored.push_reserved(movement.source_serial);
1120 }
1121 }
1122 self.stats.ignored_allocations += 1;
1123 Ok(())
1124 }
1125
1126 pub unsafe fn destroy_move(
1135 &mut self,
1136 index: usize,
1137 source: &mut Allocation,
1138 ) -> Result<(), AllocError> {
1139 if self.finished {
1140 return Err(AllocError::Closed);
1141 }
1142 let movement = self.moves.get_mut(index).ok_or(AllocError::InvalidRange)?;
1143 if source.serial != movement.source_serial {
1144 return Err(AllocError::WrongResource);
1145 }
1146 self.allocator.validate(source)?;
1147 let destination = movement
1148 .destination
1149 .as_mut()
1150 .ok_or(AllocError::UnknownAllocation)?;
1151 self.allocator.validate(destination)?;
1152 unsafe {
1157 guard::before_free(self.allocator, destination)?;
1158 guard::before_free(self.allocator, source)?;
1159 }
1160 for serial in [destination.serial, source.serial] {
1161 self
1162 .allocator
1163 .live
1164 .get_mut(&serial)
1165 .expect("validated move region")
1166 .debug_ready = false;
1167 }
1168 let before = statistics(self.allocator);
1169 unsafe { free_region(self.allocator, destination, true) }?;
1171 movement.destination = None;
1172 unsafe { free_region(self.allocator, source, true) }?;
1174 let after = statistics(self.allocator);
1175 self.stats.freed_blocks += before.native_allocation_count - after.native_allocation_count;
1176 self.stats.freed_bytes += before.native_bytes - after.native_bytes;
1177 self.stats.destroyed_allocations += 1;
1178 Ok(())
1179 }
1180
1181 pub unsafe fn abort(&mut self) -> Result<DefragmentationStats, AllocError> {
1186 for index in 0..self.moves.len() {
1187 if self.moves[index].destination.is_some() {
1188 unsafe { self.ignore_move(index) }?;
1190 }
1191 }
1192 self.finish()
1193 }
1194
1195 pub fn finish(&mut self) -> Result<DefragmentationStats, AllocError> {
1197 if self.finished {
1198 return Ok(self.stats);
1199 }
1200 if self
1201 .moves
1202 .iter()
1203 .any(|movement| movement.destination.is_some())
1204 {
1205 return Err(AllocError::LiveAllocations);
1206 }
1207 if let Some(context) = self.context.as_mut() {
1208 context.stats = add_statistics(context.stats, self.stats)?;
1209 context.active = false;
1210 }
1211 self.finished = true;
1212 Ok(self.stats)
1213 }
1214}
1215
1216pub fn set_allocation_pinned(
1218 allocator: &mut Allocator<'_>,
1219 allocation: &Allocation,
1220 pinned: bool,
1221) -> Result<(), AllocError> {
1222 allocator.validate(allocation)?;
1223 allocator
1224 .live
1225 .get_mut(&allocation.serial)
1226 .expect("validated allocation")
1227 .pinned = pinned;
1228 Ok(())
1229}
1230
1231impl<'device> Allocator<'device> {
1232 pub unsafe fn begin_defragmentation<'allocator>(
1236 &'allocator mut self,
1237 info: &DefragmentationInfo,
1238 ) -> Result<DefragmentationPass<'allocator, 'device>, AllocError> {
1239 unsafe { begin_defragmentation(self, info) }
1241 }
1242
1243 pub unsafe fn begin_defragmentation_with_break<'allocator>(
1247 &'allocator mut self,
1248 info: &DefragmentationInfo,
1249 should_break: &mut dyn FnMut() -> bool,
1250 ) -> Result<DefragmentationPass<'allocator, 'device>, AllocError> {
1251 unsafe { begin_defragmentation_with_break(self, info, should_break) }
1253 }
1254
1255 pub fn set_allocation_pinned(
1257 &mut self,
1258 allocation: &Allocation,
1259 pinned: bool,
1260 ) -> Result<(), AllocError> {
1261 set_allocation_pinned(self, allocation, pinned)
1262 }
1263}
1264
1265struct ContextState<'device> {
1266 ignored: HostVec<'device, u64>,
1267 stats: DefragmentationStats,
1268 active: bool,
1269}
1270
1271pub struct DefragmentationContext<'allocator, 'device> {
1281 allocator: &'allocator mut Allocator<'device>,
1282 pool: Option<Pool>,
1283 max_bytes: u64,
1284 max_allocations: usize,
1285 algorithm: DefragmentationAlgorithm,
1286 planning: PlanningState<'device>,
1287 state: ContextState<'device>,
1288 finished: bool,
1289}
1290
1291impl<'allocator, 'device> DefragmentationContext<'allocator, 'device> {
1292 pub fn new(
1297 allocator: &'allocator mut Allocator<'device>,
1298 info: &DefragmentationInfo,
1299 ) -> Result<Self, AllocError> {
1300 allocator.ensure_global()?;
1301 allocator.ensure_open()?;
1302 let pool = if let Some(pool) = info.pool {
1303 if pool::validate(allocator, pool)?.info.algorithm != AllocationAlgorithm::Tlsf {
1304 return Err(AllocError::UnsupportedCapability(
1305 "linear pools cannot defragment",
1306 ));
1307 }
1308 Some(Pool {
1309 owner: allocator.owner,
1310 serial: pool.serial,
1311 })
1312 } else {
1313 None
1314 };
1315 let planning = PlanningState::new(allocator, info)?;
1316 if info.algorithm == DefragmentationAlgorithm::Extensive {
1317 for block in &mut allocator.blocks {
1318 if planning
1319 .blocks
1320 .iter()
1321 .any(|selected| selected.handle == block.handle)
1322 {
1323 block.defragmentation_hold = true;
1324 }
1325 }
1326 }
1327 let state = ContextState {
1328 ignored: HostVec::new(allocator.host),
1329 stats: DefragmentationStats::new(),
1330 active: false,
1331 };
1332 Ok(Self {
1333 allocator,
1334 pool,
1335 max_bytes: info.max_bytes,
1336 max_allocations: info.max_allocations,
1337 algorithm: info.algorithm,
1338 planning,
1339 state,
1340 finished: false,
1341 })
1342 }
1343
1344 pub unsafe fn begin_pass(&mut self) -> Result<DefragmentationPass<'_, 'device>, AllocError> {
1353 unsafe { self.begin_pass_with_break(&mut || false) }
1355 }
1356
1357 pub unsafe fn begin_pass_with_break(
1364 &mut self,
1365 should_break: &mut dyn FnMut() -> bool,
1366 ) -> Result<DefragmentationPass<'_, 'device>, AllocError> {
1367 if self.finished {
1368 return Err(AllocError::Closed);
1369 }
1370 if self.state.active {
1371 return Err(AllocError::LiveAllocations);
1372 }
1373 self
1374 .state
1375 .ignored
1376 .try_reserve(self.allocator.live.len().min(self.max_allocations))
1377 .map_err(|_| AllocError::HostAllocationFailed)?;
1378 let info = DefragmentationInfo {
1379 pool: self.pool.as_ref(),
1380 max_bytes: self.max_bytes,
1381 max_allocations: self.max_allocations,
1382 algorithm: self.algorithm,
1383 };
1384 let mut pass = unsafe {
1386 plan(
1387 self.allocator,
1388 &info,
1389 &self.state.ignored,
1390 &mut self.planning,
1391 should_break,
1392 )
1393 }?;
1394 self.state.active = true;
1395 pass.context = Some(&mut self.state);
1396 Ok(pass)
1397 }
1398
1399 pub fn statistics(&self) -> DefragmentationStats {
1401 self.state.stats
1402 }
1403
1404 pub fn ignored_allocations(&self) -> usize {
1405 self.state.ignored.len()
1406 }
1407
1408 pub fn finish(&mut self) -> Result<DefragmentationStats, AllocError> {
1412 if self.state.active {
1413 return Err(AllocError::LiveAllocations);
1414 }
1415 if !self.finished && self.algorithm == DefragmentationAlgorithm::Extensive {
1416 let freed = pool::finish_defragmentation_blocks(self.allocator);
1417 self.state.stats.freed_blocks += freed.block_count;
1420 self.state.stats.freed_bytes += freed.block_bytes;
1421 }
1422 self.finished = true;
1423 Ok(self.state.stats)
1424 }
1425}
1426
1427impl Drop for DefragmentationContext<'_, '_> {
1428 fn drop(&mut self) {
1429 if self.algorithm == DefragmentationAlgorithm::Extensive {
1430 for block in &mut self.allocator.blocks {
1431 if self
1432 .planning
1433 .blocks
1434 .iter()
1435 .any(|selected| selected.handle == block.handle)
1436 {
1437 block.defragmentation_hold = false;
1438 }
1439 }
1440 }
1441 }
1442}
1443
1444fn add_statistics(
1445 a: DefragmentationStats,
1446 b: DefragmentationStats,
1447) -> Result<DefragmentationStats, AllocError> {
1448 Ok(DefragmentationStats {
1449 moved_allocations: a
1450 .moved_allocations
1451 .checked_add(b.moved_allocations)
1452 .ok_or(AllocError::InvalidRange)?,
1453 moved_bytes: a
1454 .moved_bytes
1455 .checked_add(b.moved_bytes)
1456 .ok_or(AllocError::InvalidRange)?,
1457 freed_blocks: a
1458 .freed_blocks
1459 .checked_add(b.freed_blocks)
1460 .ok_or(AllocError::InvalidRange)?,
1461 freed_bytes: a
1462 .freed_bytes
1463 .checked_add(b.freed_bytes)
1464 .ok_or(AllocError::InvalidRange)?,
1465 ignored_allocations: a
1466 .ignored_allocations
1467 .checked_add(b.ignored_allocations)
1468 .ok_or(AllocError::InvalidRange)?,
1469 destroyed_allocations: a
1470 .destroyed_allocations
1471 .checked_add(b.destroyed_allocations)
1472 .ok_or(AllocError::InvalidRange)?,
1473 })
1474}