gpu_allocator/allocator/
mod.rs

1use std::{backtrace::Backtrace, fmt, ops::Range, sync::Arc};
2
3use log::*;
4
5use crate::result::*;
6
7pub(crate) mod dedicated_block_allocator;
8pub(crate) use dedicated_block_allocator::DedicatedBlockAllocator;
9
10pub(crate) mod free_list_allocator;
11pub(crate) use free_list_allocator::FreeListAllocator;
12
13#[derive(PartialEq, Copy, Clone, Debug)]
14#[repr(u8)]
15pub(crate) enum AllocationType {
16    Free,
17    Linear,
18    NonLinear,
19}
20
21impl AllocationType {
22    #[cfg(feature = "visualizer")]
23    pub fn as_str(self) -> &'static str {
24        match self {
25            Self::Free => "Free",
26            Self::Linear => "Linear",
27            Self::NonLinear => "Non-Linear",
28        }
29    }
30}
31
32/// Describes an allocation in the [`AllocatorReport`].
33#[derive(Clone)]
34pub struct AllocationReport {
35    /// The name provided to the `allocate()` function.
36    pub name: String,
37    /// The offset in bytes of the allocation in its memory block.
38    pub offset: u64,
39    /// The size in bytes of the allocation.
40    pub size: u64,
41    #[cfg(feature = "visualizer")]
42    pub(crate) backtrace: Arc<Backtrace>,
43}
44
45/// Describes a memory block in the [`AllocatorReport`].
46#[derive(Clone)]
47pub struct MemoryBlockReport {
48    /// The size in bytes of this memory block.
49    pub size: u64,
50    /// The range of allocations in [`AllocatorReport::allocations`] that are associated
51    /// to this memory block.
52    pub allocations: Range<usize>,
53}
54
55/// A report that can be generated for informational purposes using `Allocator::generate_report()`.
56#[derive(Clone)]
57pub struct AllocatorReport {
58    /// All live allocations, sub-allocated from memory blocks.
59    pub allocations: Vec<AllocationReport>,
60    /// All memory blocks.
61    pub blocks: Vec<MemoryBlockReport>,
62    /// Sum of the memory used by all allocations, in bytes.
63    pub total_allocated_bytes: u64,
64    /// Sum of the memory reserved by all memory blocks including unallocated regions, in bytes.
65    pub total_reserved_bytes: u64,
66}
67
68impl fmt::Debug for AllocationReport {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        let name = if !self.name.is_empty() {
71            self.name.as_str()
72        } else {
73            "--"
74        };
75        write!(f, "{name:?}: {}", fmt_bytes(self.size))
76    }
77}
78
79impl fmt::Debug for AllocatorReport {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        let mut allocations = self.allocations.clone();
82        allocations.sort_by_key(|alloc| std::cmp::Reverse(alloc.size));
83
84        let max_num_allocations_to_print = f.precision().unwrap_or(usize::MAX);
85        allocations.truncate(max_num_allocations_to_print);
86
87        f.debug_struct("AllocatorReport")
88            .field(
89                "summary",
90                &std::format_args!(
91                    "{} / {}",
92                    fmt_bytes(self.total_allocated_bytes),
93                    fmt_bytes(self.total_reserved_bytes)
94                ),
95            )
96            .field("blocks", &self.blocks.len())
97            .field("allocations", &self.allocations.len())
98            .field("largest", &allocations.as_slice())
99            .finish()
100    }
101}
102
103#[cfg(feature = "visualizer")]
104pub(crate) trait SubAllocatorBase: crate::visualizer::SubAllocatorVisualizer {}
105#[cfg(not(feature = "visualizer"))]
106pub(crate) trait SubAllocatorBase {}
107
108pub(crate) trait SubAllocator: SubAllocatorBase + fmt::Debug + Sync + Send {
109    fn allocate(
110        &mut self,
111        size: u64,
112        alignment: u64,
113        allocation_type: AllocationType,
114        granularity: u64,
115        name: &str,
116        backtrace: Arc<Backtrace>,
117    ) -> Result<(u64, std::num::NonZeroU64)>;
118
119    fn free(&mut self, chunk_id: Option<std::num::NonZeroU64>) -> Result<()>;
120
121    fn rename_allocation(
122        &mut self,
123        chunk_id: Option<std::num::NonZeroU64>,
124        name: &str,
125    ) -> Result<()>;
126
127    fn report_memory_leaks(
128        &self,
129        log_level: Level,
130        memory_type_index: usize,
131        memory_block_index: usize,
132    );
133
134    fn report_allocations(&self) -> Vec<AllocationReport>;
135
136    #[must_use]
137    fn supports_general_allocations(&self) -> bool;
138    #[must_use]
139    fn allocated(&self) -> u64;
140
141    /// Helper function: reports if the suballocator is empty (meaning, having no allocations).
142    #[must_use]
143    fn is_empty(&self) -> bool {
144        self.allocated() == 0
145    }
146}
147
148pub(crate) fn fmt_bytes(mut amount: u64) -> String {
149    const SUFFIX: [&str; 5] = ["B", "KB", "MB", "GB", "TB"];
150
151    let mut idx = 0;
152    let mut print_amount = amount as f64;
153    loop {
154        if amount < 1024 {
155            return format!("{:.2} {}", print_amount, SUFFIX[idx]);
156        }
157
158        print_amount = amount as f64 / 1024.0;
159        amount /= 1024;
160        idx += 1;
161    }
162}