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 = 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 let offset = block
470 .metadata
471 .info(placement)
472 .expect("new destination placement")
473 .offset;
474 if target == source.handle && offset >= source.offset {
475 block
476 .metadata
477 .free(placement)
478 .expect("unpublished same-block placement");
479 return Ok(None);
480 }
481 let size = block.size;
482 let allocation = publish(
483 allocator,
484 Region {
485 handle: target,
486 index: source.memory_type_index,
487 size: source.size,
488 reserved_size: source.reserved_size,
489 offset,
490 alignment,
491 block_size: size,
492 placement: Some(placement),
493 },
494 );
495 let live = allocator
496 .live
497 .get_mut(&allocation.serial)
498 .expect("new destination");
499 live.pool = pool;
500 live.class = class;
501 live.can_alias = can_alias;
502 live.guard_size = guard_size;
503 live.guard_offset = guard_offset;
504 Ok(Some(allocation))
505}
506
507#[derive(Clone, Copy)]
508struct Source {
509 rank: usize,
510 offset: u64,
511 serial: u64,
512}
513
514struct PassCounters<'callback> {
515 remaining: u64,
516 oversized: u8,
517 should_break: &'callback mut dyn FnMut() -> bool,
518 interrupted: bool,
519 panic: Option<Box<dyn std::any::Any + Send>>,
520}
521
522const MAX_OVERSIZED_ALLOCATIONS: u8 = 16;
524
525struct Scan<'a> {
526 blocks: &'a [OrderedBlock],
527 sources: &'a [Source],
528 max_allocations: usize,
529}
530
531#[derive(Clone, Copy)]
532enum Within {
533 Never,
534 Full,
535 Balanced(BalancedState),
536}
537
538#[derive(Clone, Copy)]
539enum ResourceGroup {
540 Textures,
541 Buffers,
542 All,
543}
544
545impl ResourceGroup {
546 fn admits(self, class: ResourceClass) -> bool {
547 match self {
548 Self::Textures => class == ResourceClass::OptimalImage,
549 Self::Buffers => matches!(class, ResourceClass::Buffer | ResourceClass::LinearImage),
550 Self::All => true,
551 }
552 }
553}
554
555struct Selection {
556 sources: std::ops::Range<usize>,
557 targets: std::ops::Range<usize>,
558 earlier_only: bool,
559 within: Within,
560 group: ResourceGroup,
561}
562
563impl PassCounters<'_> {
564 fn exhausted(&self, max_allocations: usize, moves: usize) -> bool {
565 self.interrupted
566 || moves == max_allocations
567 || self.remaining == 0
568 || self.oversized == MAX_OVERSIZED_ALLOCATIONS
569 }
570
571 fn poll(&mut self) -> bool {
572 match std::panic::catch_unwind(std::panic::AssertUnwindSafe(&mut self.should_break)) {
573 Ok(false) => false,
574 Ok(true) => {
575 self.interrupted = true;
576 true
577 }
578 Err(payload) => {
579 self.interrupted = true;
580 self.panic = Some(payload);
581 true
582 }
583 }
584 }
585}
586
587unsafe fn prepare_destination(
591 allocator: &mut Allocator<'_>,
592 source: &Allocation,
593 destination: &Allocation,
594) -> Result<(), AllocError> {
595 unsafe { guard::initialize(allocator, destination) }?;
597 let live = allocator.validate(source)?;
598 let map_count = live.map_count;
599 let persistent = live.persistent;
600 let holds = map_count
601 .checked_add(u32::from(persistent))
602 .ok_or(AllocError::InvalidRange)?;
603 if holds != 0 {
604 let device = allocator.device.clone();
605 unsafe {
608 allocator.mapping_mut(destination).acquire_many(
609 device.as_device(),
610 destination.handle,
611 destination.block_size,
612 holds,
613 )
614 }?;
615 let live = allocator
616 .live
617 .get_mut(&destination.serial)
618 .expect("new destination");
619 live.map_count = map_count;
620 live.persistent = persistent;
621 }
622 Ok(())
623}
624
625unsafe fn record_move(
626 allocator: &mut Allocator<'_>,
627 source: &Allocation,
628 mut destination: Allocation,
629 moves: &mut HostVec<'_, Move>,
630 counters: &mut PassCounters,
631) -> Result<(), AllocError> {
632 if let Err(error) = unsafe { prepare_destination(allocator, source, &destination) } {
634 unsafe { rollback_allocation(allocator, &mut destination) }
636 .expect("debug destination rollback");
637 return Err(error);
638 }
639 let live = &allocator.live[&source.serial];
640 let resource = live.resource;
641 let can_alias = live.can_alias;
642 let destination_info =
643 allocation_info(allocator, &destination).expect("new destination identity");
644 let source_info = allocation_info(allocator, source).expect("source unchanged during planning");
645 counters.remaining -= source.size;
646 moves.push_reserved(Move {
647 source_serial: source.serial,
648 destination: Some(destination),
649 proposal: DefragmentationMove {
650 source: source_info,
651 destination: destination_info,
652 resource: match resource {
653 Some(Resource::Buffer(buffer)) => DefragmentationResource::Buffer(buffer),
654 Some(Resource::Image(image)) => DefragmentationResource::Image(image),
655 None => DefragmentationResource::Unassociated,
656 },
657 can_alias,
658 },
659 });
660 Ok(())
661}
662
663unsafe fn plan_selected(
664 allocator: &mut Allocator<'_>,
665 scan: &Scan<'_>,
666 selection: &Selection,
667 moves: &mut HostVec<'_, Move>,
668 counters: &mut PassCounters,
669) -> Result<(), AllocError> {
670 for source in scan
671 .sources
672 .iter()
673 .filter(|source| selection.sources.contains(&source.rank))
674 {
675 if counters.exhausted(scan.max_allocations, moves.len()) {
676 break;
677 }
678 if moves
679 .iter()
680 .any(|movement| movement.source_serial == source.serial)
681 {
682 continue;
683 }
684 if counters.poll() {
685 break;
686 }
687 let token = allocator.token(source.serial)?;
688 if token.size > counters.remaining {
689 counters.oversized += 1;
690 continue;
691 }
692 counters.oversized = 0;
693 if !selection.group.admits(allocator.live[&source.serial].class) {
694 continue;
695 }
696 let mut destination = None;
697 for rank in selection.targets.clone() {
698 let block = &scan.blocks[rank];
699 if block.handle == token.handle
700 || block.domain != scan.blocks[source.rank].domain
701 || selection.earlier_only && rank >= source.rank
702 {
703 continue;
704 }
705 if let Some(allocation) = reserve_destination(allocator, &token, block.handle)? {
706 destination = Some(allocation);
707 break;
708 }
709 }
710 if destination.is_none()
711 && token.offset != 0
712 && match selection.within {
713 Within::Never => false,
714 Within::Full => true,
715 Within::Balanced(averages) => worthwhile_same_block(allocator, &token, averages),
716 } {
717 destination = reserve_destination(allocator, &token, token.handle)?;
718 }
719 if let Some(destination) = destination {
720 unsafe { record_move(allocator, &token, destination, moves, counters) }?;
722 }
723 }
724 Ok(())
725}
726
727unsafe fn plan<'allocator, 'device>(
728 allocator: &'allocator mut Allocator<'device>,
729 info: &DefragmentationInfo,
730 ignored: &[u64],
731 state: &mut PlanningState<'device>,
732 should_break: &mut dyn FnMut() -> bool,
733) -> Result<DefragmentationPass<'allocator, 'device>, AllocError> {
734 allocator.ensure_open()?;
735 let mut blocks = HostVec::new(allocator.host);
736 blocks
737 .try_reserve(state.blocks.len())
738 .map_err(|_| AllocError::HostAllocationFailed)?;
739 for block in &state.blocks {
740 if allocator
741 .blocks
742 .iter()
743 .any(|live| live.handle == block.handle)
744 {
745 blocks.push_reserved(OrderedBlock {
746 immovable: ignored.iter().any(|serial| {
747 allocator
748 .live
749 .get(serial)
750 .is_some_and(|live| live.handle == block.handle)
751 }),
752 ..*block
753 });
754 }
755 }
756 let mut domains = HostVec::new(allocator.host);
757 domains
758 .try_reserve(state.domains.len())
759 .map_err(|_| AllocError::HostAllocationFailed)?;
760 domains.extend_from_slice_reserved(&state.domains);
761 if info.algorithm == DefragmentationAlgorithm::Extensive && allocator.granularity.0 != Some(1) {
764 extensive::exclude_ignored(&mut blocks, &mut domains);
765 } else {
766 blocks.sort_unstable_by_key(|block| (block.domain, !block.immovable, block.rank));
767 }
768 let mut sources = HostVec::new(allocator.host);
769 sources
770 .try_reserve(allocator.live.len())
771 .map_err(|_| AllocError::HostAllocationFailed)?;
772 for (&serial, live) in &allocator.live {
773 if live.plane.is_some() || live.imported || live.pinned || live.placement.is_none() {
774 continue;
775 }
776 if let Some(rank) = blocks
777 .iter()
778 .position(|block| block.handle == live.handle && !block.immovable)
779 {
780 sources.push_reserved(Source {
781 rank,
782 offset: live.offset,
783 serial,
784 });
785 }
786 }
787 sources.sort_unstable_by(|left, right| {
788 right
789 .rank
790 .cmp(&left.rank)
791 .then(left.offset.cmp(&right.offset))
792 .then(left.serial.cmp(&right.serial))
793 });
794 let mut moves = HostVec::new(allocator.host);
795 moves
796 .try_reserve(sources.len().min(info.max_allocations))
797 .map_err(|_| AllocError::HostAllocationFailed)?;
798 let mut counters = PassCounters {
799 remaining: info.max_bytes,
800 oversized: 0,
801 should_break,
802 interrupted: false,
803 panic: None,
804 };
805 let planned = (|| -> Result<(), AllocError> {
806 for domain in &mut domains {
807 if counters.exhausted(info.max_allocations, moves.len()) {
808 break;
809 }
810 let Some(start) = blocks.iter().position(|block| block.domain == domain.key) else {
811 continue;
812 };
813 let end = blocks[start..]
814 .iter()
815 .position(|block| block.domain != domain.key)
816 .map_or(blocks.len(), |length| start + length);
817 let scan = Scan {
818 blocks: &blocks,
819 sources: &sources,
820 max_allocations: info.max_allocations,
821 };
822 if info.algorithm == DefragmentationAlgorithm::Extensive && allocator.granularity.0 != Some(1)
823 {
824 unsafe {
826 extensive::plan(
827 allocator,
828 &scan,
829 start..end,
830 &mut domain.extensive,
831 &mut moves,
832 &mut counters,
833 )
834 }?;
835 continue;
836 }
837 let within = match info.algorithm {
838 DefragmentationAlgorithm::Fast => Within::Never,
839 DefragmentationAlgorithm::Balanced => Within::Balanced(domain.balanced),
840 DefragmentationAlgorithm::Full | DefragmentationAlgorithm::Extensive => Within::Full,
841 };
842 let mut selection = Selection {
843 sources: start..end,
844 targets: start..end,
845 earlier_only: true,
846 within,
847 group: ResourceGroup::All,
848 };
849 let before = moves.len();
850 unsafe { plan_selected(allocator, &scan, &selection, &mut moves, &mut counters) }?;
852 if before == moves.len()
853 && info.algorithm == DefragmentationAlgorithm::Balanced
854 && !counters.exhausted(info.max_allocations, moves.len())
855 {
856 let refreshed = vector_statistics(allocator, domain.key);
857 if refreshed != domain.balanced {
858 domain.balanced = refreshed;
859 selection.within = Within::Balanced(refreshed);
860 unsafe { plan_selected(allocator, &scan, &selection, &mut moves, &mut counters) }?;
862 }
863 }
864 }
865 Ok(())
866 })();
867 if planned.is_err() || counters.panic.is_some() {
868 for movement in &mut moves {
869 unsafe {
871 rollback_allocation(
872 allocator,
873 movement.destination.as_mut().expect("unpublished move"),
874 )
875 }
876 .expect("planning rollback");
877 }
878 if let Some(payload) = counters.panic.take() {
879 std::panic::resume_unwind(payload);
880 }
881 planned?;
882 }
883 state.blocks = blocks;
884 state.domains = domains;
885 Ok(DefragmentationPass {
886 allocator,
887 moves,
888 stats: DefragmentationStats::new(),
889 finished: false,
890 interrupted: counters.interrupted,
891 context: None,
892 })
893}
894
895impl DefragmentationPass<'_, '_> {
896 pub fn len(&self) -> usize {
898 self.moves.len()
899 }
900
901 pub fn is_empty(&self) -> bool {
902 self.moves.is_empty()
903 }
904
905 pub fn was_interrupted(&self) -> bool {
908 self.interrupted
909 }
910
911 pub fn movement(&self, index: usize) -> Result<&DefragmentationMove, AllocError> {
913 Ok(
914 &self
915 .moves
916 .get(index)
917 .ok_or(AllocError::InvalidRange)?
918 .proposal,
919 )
920 }
921
922 pub fn device(&self) -> &vk::Device {
924 self.allocator.device.as_device()
925 }
926
927 pub fn allocation_callbacks(&self) -> Option<&vk::AllocationCallbacks> {
929 self.allocator.callbacks
930 }
931
932 pub unsafe fn bind_buffer(&mut self, index: usize, buffer: vk::Buffer) -> Result<(), AllocError> {
937 if self.finished {
938 return Err(AllocError::Closed);
939 }
940 let movement = self.moves.get(index).ok_or(AllocError::InvalidRange)?;
941 if !matches!(
942 movement.proposal.resource,
943 DefragmentationResource::Buffer(_) | DefragmentationResource::Unassociated
944 ) {
945 return Err(AllocError::WrongResource);
946 }
947 let destination = movement
948 .destination
949 .as_ref()
950 .ok_or(AllocError::UnknownAllocation)?;
951 unsafe { bind_buffer_memory(self.allocator, buffer, destination) }
953 }
954
955 pub unsafe fn bind_image(&mut self, index: usize, image: vk::Image) -> Result<(), AllocError> {
959 if self.finished {
960 return Err(AllocError::Closed);
961 }
962 let movement = self.moves.get(index).ok_or(AllocError::InvalidRange)?;
963 if !matches!(
964 movement.proposal.resource,
965 DefragmentationResource::Image(_) | DefragmentationResource::Unassociated
966 ) {
967 return Err(AllocError::WrongResource);
968 }
969 let destination = movement
970 .destination
971 .as_ref()
972 .ok_or(AllocError::UnknownAllocation)?;
973 unsafe { bind_image_memory(self.allocator, image, destination) }
975 }
976
977 pub unsafe fn create_aliasing_buffer(
985 &mut self,
986 index: usize,
987 local_offset: u64,
988 info: &vk::BufferCreateInfo,
989 ) -> Result<vk::Buffer, AllocError> {
990 let destination = self.destination(index)?;
991 let token = self.allocator.token(destination.serial)?;
992 unsafe { create_aliasing_buffer(self.allocator, &token, local_offset, info) }
994 }
995
996 pub unsafe fn create_aliasing_image(
1002 &mut self,
1003 index: usize,
1004 local_offset: u64,
1005 info: &vk::ImageCreateInfo,
1006 ) -> Result<vk::Image, AllocError> {
1007 let destination = self.destination(index)?;
1008 let token = self.allocator.token(destination.serial)?;
1009 unsafe { create_aliasing_image(self.allocator, &token, local_offset, info) }
1011 }
1012
1013 fn destination(&self, index: usize) -> Result<&Allocation, AllocError> {
1014 if self.finished {
1015 return Err(AllocError::Closed);
1016 }
1017 self
1018 .moves
1019 .get(index)
1020 .ok_or(AllocError::InvalidRange)?
1021 .destination
1022 .as_ref()
1023 .ok_or(AllocError::UnknownAllocation)
1024 }
1025
1026 pub unsafe fn commit_move(
1043 &mut self,
1044 index: usize,
1045 source: &mut Allocation,
1046 ) -> Result<(), AllocError> {
1047 if self.finished {
1048 return Err(AllocError::Closed);
1049 }
1050 let movement = self.moves.get_mut(index).ok_or(AllocError::InvalidRange)?;
1051 if source.serial != movement.source_serial {
1052 return Err(AllocError::WrongResource);
1053 }
1054 let old = self.allocator.validate(source)?;
1055 let destination = movement
1056 .destination
1057 .as_ref()
1058 .ok_or(AllocError::UnknownAllocation)?;
1059 if (old.bound || old.resource.is_some()) && !self.allocator.validate(destination)?.bound {
1060 return Err(AllocError::InvalidRequest("replacement is not bound"));
1061 }
1062 let user_data = old.user_data;
1063 let pinned = old.pinned;
1064 let name = self
1065 .allocator
1066 .live
1067 .get_mut(&source.serial)
1068 .expect("validated source")
1069 .name
1070 .take();
1071 let old_serial = source.serial;
1072 let before = statistics(self.allocator);
1073 if let Err(error) = unsafe { free_region(self.allocator, source, true) } {
1075 self
1076 .allocator
1077 .live
1078 .get_mut(&old_serial)
1079 .expect("retained failed source")
1080 .name = name;
1081 return Err(error);
1082 }
1083 let destination = movement.destination.take().expect("validated destination");
1084 let live = self
1085 .allocator
1086 .live
1087 .get_mut(&destination.serial)
1088 .expect("live destination");
1089 live.name = name;
1090 live.user_data = user_data;
1091 live.pinned = pinned;
1092 *source = destination;
1093 let after = statistics(self.allocator);
1094 self.stats.moved_allocations += 1;
1095 self.stats.moved_bytes += source.size;
1096 self.stats.freed_blocks += before.native_allocation_count - after.native_allocation_count;
1097 self.stats.freed_bytes += before.native_bytes - after.native_bytes;
1098 Ok(())
1099 }
1100
1101 pub unsafe fn ignore_move(&mut self, index: usize) -> Result<(), AllocError> {
1106 if self.finished {
1107 return Err(AllocError::Closed);
1108 }
1109 let movement = self.moves.get_mut(index).ok_or(AllocError::InvalidRange)?;
1110 let destination = movement
1111 .destination
1112 .as_mut()
1113 .ok_or(AllocError::UnknownAllocation)?;
1114 let before = statistics(self.allocator);
1115 unsafe { free_region(self.allocator, destination, true) }?;
1117 let after = statistics(self.allocator);
1118 self.stats.freed_blocks += before.native_allocation_count - after.native_allocation_count;
1119 self.stats.freed_bytes += before.native_bytes - after.native_bytes;
1120 movement.destination = None;
1121 if let Some(context) = self.context.as_mut() {
1122 if !context.ignored.contains(&movement.source_serial) {
1124 context.ignored.push_reserved(movement.source_serial);
1125 }
1126 }
1127 self.stats.ignored_allocations += 1;
1128 Ok(())
1129 }
1130
1131 pub unsafe fn destroy_move(
1140 &mut self,
1141 index: usize,
1142 source: &mut Allocation,
1143 ) -> Result<(), AllocError> {
1144 if self.finished {
1145 return Err(AllocError::Closed);
1146 }
1147 let movement = self.moves.get_mut(index).ok_or(AllocError::InvalidRange)?;
1148 if source.serial != movement.source_serial {
1149 return Err(AllocError::WrongResource);
1150 }
1151 self.allocator.validate(source)?;
1152 let destination = movement
1153 .destination
1154 .as_mut()
1155 .ok_or(AllocError::UnknownAllocation)?;
1156 self.allocator.validate(destination)?;
1157 unsafe {
1162 guard::before_free(self.allocator, destination)?;
1163 guard::before_free(self.allocator, source)?;
1164 }
1165 for serial in [destination.serial, source.serial] {
1166 self
1167 .allocator
1168 .live
1169 .get_mut(&serial)
1170 .expect("validated move region")
1171 .debug_ready = false;
1172 }
1173 let before = statistics(self.allocator);
1174 unsafe { free_region(self.allocator, destination, true) }?;
1176 movement.destination = None;
1177 unsafe { free_region(self.allocator, source, true) }?;
1179 let after = statistics(self.allocator);
1180 self.stats.freed_blocks += before.native_allocation_count - after.native_allocation_count;
1181 self.stats.freed_bytes += before.native_bytes - after.native_bytes;
1182 self.stats.destroyed_allocations += 1;
1183 Ok(())
1184 }
1185
1186 pub unsafe fn abort(&mut self) -> Result<DefragmentationStats, AllocError> {
1191 for index in 0..self.moves.len() {
1192 if self.moves[index].destination.is_some() {
1193 unsafe { self.ignore_move(index) }?;
1195 }
1196 }
1197 self.finish()
1198 }
1199
1200 pub fn finish(&mut self) -> Result<DefragmentationStats, AllocError> {
1202 if self.finished {
1203 return Ok(self.stats);
1204 }
1205 if self
1206 .moves
1207 .iter()
1208 .any(|movement| movement.destination.is_some())
1209 {
1210 return Err(AllocError::LiveAllocations);
1211 }
1212 if let Some(context) = self.context.as_mut() {
1213 context.stats = add_statistics(context.stats, self.stats)?;
1214 context.active = false;
1215 }
1216 self.finished = true;
1217 Ok(self.stats)
1218 }
1219}
1220
1221pub fn set_allocation_pinned(
1223 allocator: &mut Allocator<'_>,
1224 allocation: &Allocation,
1225 pinned: bool,
1226) -> Result<(), AllocError> {
1227 allocator.validate(allocation)?;
1228 allocator
1229 .live
1230 .get_mut(&allocation.serial)
1231 .expect("validated allocation")
1232 .pinned = pinned;
1233 Ok(())
1234}
1235
1236impl<'device> Allocator<'device> {
1237 pub unsafe fn begin_defragmentation<'allocator>(
1241 &'allocator mut self,
1242 info: &DefragmentationInfo,
1243 ) -> Result<DefragmentationPass<'allocator, 'device>, AllocError> {
1244 unsafe { begin_defragmentation(self, info) }
1246 }
1247
1248 pub unsafe fn begin_defragmentation_with_break<'allocator>(
1252 &'allocator mut self,
1253 info: &DefragmentationInfo,
1254 should_break: &mut dyn FnMut() -> bool,
1255 ) -> Result<DefragmentationPass<'allocator, 'device>, AllocError> {
1256 unsafe { begin_defragmentation_with_break(self, info, should_break) }
1258 }
1259
1260 pub fn set_allocation_pinned(
1262 &mut self,
1263 allocation: &Allocation,
1264 pinned: bool,
1265 ) -> Result<(), AllocError> {
1266 set_allocation_pinned(self, allocation, pinned)
1267 }
1268}
1269
1270struct ContextState<'device> {
1271 ignored: HostVec<'device, u64>,
1272 stats: DefragmentationStats,
1273 active: bool,
1274}
1275
1276pub struct DefragmentationContext<'allocator, 'device> {
1286 allocator: &'allocator mut Allocator<'device>,
1287 pool: Option<Pool>,
1288 max_bytes: u64,
1289 max_allocations: usize,
1290 algorithm: DefragmentationAlgorithm,
1291 planning: PlanningState<'device>,
1292 state: ContextState<'device>,
1293 finished: bool,
1294}
1295
1296impl<'allocator, 'device> DefragmentationContext<'allocator, 'device> {
1297 pub fn new(
1302 allocator: &'allocator mut Allocator<'device>,
1303 info: &DefragmentationInfo,
1304 ) -> Result<Self, AllocError> {
1305 allocator.ensure_global()?;
1306 allocator.ensure_open()?;
1307 let pool = if let Some(pool) = info.pool {
1308 if pool::validate(allocator, pool)?.info.algorithm != AllocationAlgorithm::Tlsf {
1309 return Err(AllocError::UnsupportedCapability(
1310 "linear pools cannot defragment",
1311 ));
1312 }
1313 Some(Pool {
1314 owner: allocator.owner,
1315 serial: pool.serial,
1316 })
1317 } else {
1318 None
1319 };
1320 let planning = PlanningState::new(allocator, info)?;
1321 if info.algorithm == DefragmentationAlgorithm::Extensive {
1322 for block in &mut allocator.blocks {
1323 if planning
1324 .blocks
1325 .iter()
1326 .any(|selected| selected.handle == block.handle)
1327 {
1328 block.defragmentation_hold = true;
1329 }
1330 }
1331 }
1332 let state = ContextState {
1333 ignored: HostVec::new(allocator.host),
1334 stats: DefragmentationStats::new(),
1335 active: false,
1336 };
1337 Ok(Self {
1338 allocator,
1339 pool,
1340 max_bytes: info.max_bytes,
1341 max_allocations: info.max_allocations,
1342 algorithm: info.algorithm,
1343 planning,
1344 state,
1345 finished: false,
1346 })
1347 }
1348
1349 pub unsafe fn begin_pass(&mut self) -> Result<DefragmentationPass<'_, 'device>, AllocError> {
1358 unsafe { self.begin_pass_with_break(&mut || false) }
1360 }
1361
1362 pub unsafe fn begin_pass_with_break(
1369 &mut self,
1370 should_break: &mut dyn FnMut() -> bool,
1371 ) -> Result<DefragmentationPass<'_, 'device>, AllocError> {
1372 if self.finished {
1373 return Err(AllocError::Closed);
1374 }
1375 if self.state.active {
1376 return Err(AllocError::LiveAllocations);
1377 }
1378 self
1379 .state
1380 .ignored
1381 .try_reserve(self.allocator.live.len().min(self.max_allocations))
1382 .map_err(|_| AllocError::HostAllocationFailed)?;
1383 let info = DefragmentationInfo {
1384 pool: self.pool.as_ref(),
1385 max_bytes: self.max_bytes,
1386 max_allocations: self.max_allocations,
1387 algorithm: self.algorithm,
1388 };
1389 let mut pass = unsafe {
1391 plan(
1392 self.allocator,
1393 &info,
1394 &self.state.ignored,
1395 &mut self.planning,
1396 should_break,
1397 )
1398 }?;
1399 self.state.active = true;
1400 pass.context = Some(&mut self.state);
1401 Ok(pass)
1402 }
1403
1404 pub fn statistics(&self) -> DefragmentationStats {
1406 self.state.stats
1407 }
1408
1409 pub fn ignored_allocations(&self) -> usize {
1410 self.state.ignored.len()
1411 }
1412
1413 pub fn finish(&mut self) -> Result<DefragmentationStats, AllocError> {
1417 if self.state.active {
1418 return Err(AllocError::LiveAllocations);
1419 }
1420 if !self.finished && self.algorithm == DefragmentationAlgorithm::Extensive {
1421 let freed = pool::finish_defragmentation_blocks(self.allocator);
1422 self.state.stats.freed_blocks += freed.block_count;
1425 self.state.stats.freed_bytes += freed.block_bytes;
1426 }
1427 self.finished = true;
1428 Ok(self.state.stats)
1429 }
1430}
1431
1432impl Drop for DefragmentationContext<'_, '_> {
1433 fn drop(&mut self) {
1434 if self.algorithm == DefragmentationAlgorithm::Extensive {
1435 for block in &mut self.allocator.blocks {
1436 if self
1437 .planning
1438 .blocks
1439 .iter()
1440 .any(|selected| selected.handle == block.handle)
1441 {
1442 block.defragmentation_hold = false;
1443 }
1444 }
1445 }
1446 }
1447}
1448
1449fn add_statistics(
1450 a: DefragmentationStats,
1451 b: DefragmentationStats,
1452) -> Result<DefragmentationStats, AllocError> {
1453 Ok(DefragmentationStats {
1454 moved_allocations: a
1455 .moved_allocations
1456 .checked_add(b.moved_allocations)
1457 .ok_or(AllocError::InvalidRange)?,
1458 moved_bytes: a
1459 .moved_bytes
1460 .checked_add(b.moved_bytes)
1461 .ok_or(AllocError::InvalidRange)?,
1462 freed_blocks: a
1463 .freed_blocks
1464 .checked_add(b.freed_blocks)
1465 .ok_or(AllocError::InvalidRange)?,
1466 freed_bytes: a
1467 .freed_bytes
1468 .checked_add(b.freed_bytes)
1469 .ok_or(AllocError::InvalidRange)?,
1470 ignored_allocations: a
1471 .ignored_allocations
1472 .checked_add(b.ignored_allocations)
1473 .ok_or(AllocError::InvalidRange)?,
1474 destroyed_allocations: a
1475 .destroyed_allocations
1476 .checked_add(b.destroyed_allocations)
1477 .ok_or(AllocError::InvalidRange)?,
1478 })
1479}