1use alloc::{string::String, vec::Vec};
2#[cfg(feature = "counters")]
3use core::sync::atomic::{AtomicIsize, Ordering};
4use core::{fmt, ops::Range};
5
6pub struct InternalCounter {
11    #[cfg(feature = "counters")]
12    value: AtomicIsize,
13}
14
15impl InternalCounter {
16    #[inline]
18    #[must_use]
19    pub const fn new() -> Self {
20        InternalCounter {
21            #[cfg(feature = "counters")]
22            value: AtomicIsize::new(0),
23        }
24    }
25
26    #[cfg(feature = "counters")]
28    #[inline]
29    pub fn read(&self) -> isize {
30        self.value.load(Ordering::Relaxed)
31    }
32
33    #[cfg(not(feature = "counters"))]
37    #[inline]
38    #[must_use]
39    pub fn read(&self) -> isize {
40        0
41    }
42
43    #[cfg(feature = "counters")]
47    #[inline]
48    pub fn take(&self) -> isize {
49        self.value.swap(0, Ordering::Relaxed)
50    }
51
52    #[cfg(not(feature = "counters"))]
56    #[inline]
57    #[must_use]
58    pub fn take(&self) -> isize {
59        0
60    }
61
62    #[inline]
64    pub fn add(&self, _val: isize) {
65        #[cfg(feature = "counters")]
66        self.value.fetch_add(_val, Ordering::Relaxed);
67    }
68
69    #[inline]
71    pub fn sub(&self, _val: isize) {
72        #[cfg(feature = "counters")]
73        self.value.fetch_add(-_val, Ordering::Relaxed);
74    }
75
76    #[inline]
78    pub fn set(&self, _val: isize) {
79        #[cfg(feature = "counters")]
80        self.value.store(_val, Ordering::Relaxed);
81    }
82}
83
84impl Clone for InternalCounter {
85    fn clone(&self) -> Self {
86        InternalCounter {
87            #[cfg(feature = "counters")]
88            value: AtomicIsize::new(self.read()),
89        }
90    }
91}
92
93impl Default for InternalCounter {
94    fn default() -> Self {
95        Self::new()
96    }
97}
98
99impl core::fmt::Debug for InternalCounter {
100    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101        self.read().fmt(f)
102    }
103}
104
105#[allow(missing_docs)]
107#[derive(Clone, Default)]
108pub struct HalCounters {
109    pub buffers: InternalCounter,
111    pub textures: InternalCounter,
112    pub texture_views: InternalCounter,
113    pub bind_groups: InternalCounter,
114    pub bind_group_layouts: InternalCounter,
115    pub render_pipelines: InternalCounter,
116    pub compute_pipelines: InternalCounter,
117    pub pipeline_layouts: InternalCounter,
118    pub samplers: InternalCounter,
119    pub command_encoders: InternalCounter,
120    pub shader_modules: InternalCounter,
121    pub query_sets: InternalCounter,
122    pub fences: InternalCounter,
123
124    pub buffer_memory: InternalCounter,
127    pub texture_memory: InternalCounter,
129    pub acceleration_structure_memory: InternalCounter,
131    pub memory_allocations: InternalCounter,
133}
134
135#[derive(Clone, Default)]
137pub struct CoreCounters {
138    }
140
141#[derive(Clone, Default)]
146pub struct InternalCounters {
147    pub core: CoreCounters,
149    pub hal: HalCounters,
151}
152
153#[derive(Clone)]
155pub struct AllocationReport {
156    pub name: String,
158    pub offset: u64,
160    pub size: u64,
162}
163
164#[derive(Clone)]
166pub struct MemoryBlockReport {
167    pub size: u64,
169    pub allocations: Range<usize>,
172}
173
174#[derive(Clone)]
176pub struct AllocatorReport {
177    pub allocations: Vec<AllocationReport>,
179    pub blocks: Vec<MemoryBlockReport>,
181    pub total_allocated_bytes: u64,
183    pub total_reserved_bytes: u64,
185}
186
187impl fmt::Debug for AllocationReport {
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        let name = if !self.name.is_empty() {
190            self.name.as_str()
191        } else {
192            "--"
193        };
194        write!(f, "{name:?}: {}", FmtBytes(self.size))
195    }
196}
197
198impl fmt::Debug for AllocatorReport {
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        let mut allocations = self.allocations.clone();
201        allocations.sort_by_key(|alloc| core::cmp::Reverse(alloc.size));
202
203        let max_num_allocations_to_print = f.precision().unwrap_or(usize::MAX);
204        allocations.truncate(max_num_allocations_to_print);
205
206        f.debug_struct("AllocatorReport")
207            .field(
208                "summary",
209                &core::format_args!(
210                    "{} / {}",
211                    FmtBytes(self.total_allocated_bytes),
212                    FmtBytes(self.total_reserved_bytes)
213                ),
214            )
215            .field("blocks", &self.blocks.len())
216            .field("allocations", &self.allocations.len())
217            .field("largest", &allocations.as_slice())
218            .finish()
219    }
220}
221
222struct FmtBytes(u64);
223
224impl fmt::Display for FmtBytes {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        const SUFFIX: [&str; 5] = ["B", "KB", "MB", "GB", "TB"];
227        let mut idx = 0;
228        let mut amount = self.0 as f64;
229        loop {
230            if amount < 1024.0 || idx == SUFFIX.len() - 1 {
231                return write!(f, "{:.2} {}", amount, SUFFIX[idx]);
232            }
233
234            amount /= 1024.0;
235            idx += 1;
236        }
237    }
238}