1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
use crate::ffi;
use crate::Allocator;
use ash::prelude::VkResult;
use ash::vk;

pub use ffi::VmaDefragmentationMove as DefragmentationMove;
pub use ffi::VmaDefragmentationStats as DefragmentationStats;
pub struct DefragmentationContext<'a> {
    allocator: &'a Allocator,
    raw: ffi::VmaDefragmentationContext,
}

impl<'a> Drop for DefragmentationContext<'a> {
    fn drop(&mut self) {
        unsafe {
            ffi::vmaEndDefragmentation(self.allocator.internal, self.raw, std::ptr::null_mut());
        }
    }
}

impl<'a> DefragmentationContext<'a> {
    /// Ends defragmentation process.
    pub fn end(self) -> DefragmentationStats {
        let mut stats = DefragmentationStats {
            bytesMoved: 0,
            bytesFreed: 0,
            allocationsMoved: 0,
            deviceMemoryBlocksFreed: 0,
        };
        unsafe {
            ffi::vmaEndDefragmentation(self.allocator.internal, self.raw, &mut stats);
        }
        std::mem::forget(self);
        stats
    }

    /// Returns `false` if no more moves are possible or `true` if more defragmentations are possible.
    pub fn begin_pass(&self, mover: impl FnOnce(&mut [DefragmentationMove]) -> ()) -> bool {
        let mut pass_info = ffi::VmaDefragmentationPassMoveInfo {
            moveCount: 0,
            pMoves: std::ptr::null_mut(),
        };
        let result = unsafe {
            ffi::vmaBeginDefragmentationPass(self.allocator.internal, self.raw, &mut pass_info)
        };
        if result == vk::Result::SUCCESS {
            return false;
        }
        debug_assert_eq!(result, vk::Result::INCOMPLETE);
        let moves = unsafe {
            std::slice::from_raw_parts_mut(pass_info.pMoves, pass_info.moveCount as usize)
        };
        mover(moves);

        let result = unsafe {
            ffi::vmaEndDefragmentationPass(self.allocator.internal, self.raw, &mut pass_info)
        };

        return result == vk::Result::INCOMPLETE;
    }
}

impl Allocator {
    /// Begins defragmentation process.
    ///
    /// ## Returns
    /// `VK_SUCCESS` if defragmentation can begin.
    /// `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported.
    pub unsafe fn begin_defragmentation(
        &self,
        info: &ffi::VmaDefragmentationInfo,
    ) -> VkResult<DefragmentationContext> {
        let mut context: ffi::VmaDefragmentationContext = std::ptr::null_mut();

        ffi::vmaBeginDefragmentation(self.internal, info, &mut context).result()?;

        Ok(DefragmentationContext {
            allocator: self,
            raw: context,
        })
    }
}