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 fmt::Debug for InternalCounter {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 self.read().fmt(f)
102 }
103}
104
105#[allow(missing_docs)]
107#[derive(Clone, Debug, 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 ray_tracing_pipelines: InternalCounter,
118 pub pipeline_layouts: InternalCounter,
119 pub samplers: InternalCounter,
120 pub command_encoders: InternalCounter,
121 pub shader_modules: InternalCounter,
122 pub query_sets: InternalCounter,
123 pub fences: InternalCounter,
124
125 pub buffer_memory: InternalCounter,
128 pub texture_memory: InternalCounter,
130 pub acceleration_structure_memory: InternalCounter,
132 pub memory_allocations: InternalCounter,
134}
135
136#[derive(Clone, Debug, Default)]
138pub struct CoreCounters {
139 }
141
142#[derive(Clone, Debug, Default)]
147pub struct InternalCounters {
148 pub core: CoreCounters,
150 pub hal: HalCounters,
152}
153
154#[derive(Clone)]
156pub struct AllocationReport {
157 pub name: String,
159 pub offset: u64,
161 pub size: u64,
163}
164
165#[derive(Clone, Debug)]
167pub struct MemoryBlockReport {
168 pub size: u64,
170 pub allocations: Range<usize>,
173}
174
175#[derive(Clone)]
177pub struct AllocatorReport {
178 pub allocations: Vec<AllocationReport>,
180 pub blocks: Vec<MemoryBlockReport>,
182 pub total_allocated_bytes: u64,
184 pub total_reserved_bytes: u64,
187}
188
189impl fmt::Debug for AllocationReport {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 let name = if !self.name.is_empty() {
192 self.name.as_str()
193 } else {
194 "--"
195 };
196 write!(f, "{name:?}: {}", FmtBytes(self.size))
197 }
198}
199
200impl fmt::Debug for AllocatorReport {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 let mut allocations = self.allocations.clone();
203 allocations.sort_by_key(|alloc| core::cmp::Reverse(alloc.size));
204
205 let max_num_allocations_to_print = f.precision().unwrap_or(usize::MAX);
206 allocations.truncate(max_num_allocations_to_print);
207
208 f.debug_struct("AllocatorReport")
209 .field(
210 "summary",
211 &core::format_args!(
212 "{} / {}",
213 FmtBytes(self.total_allocated_bytes),
214 FmtBytes(self.total_reserved_bytes)
215 ),
216 )
217 .field("blocks", &self.blocks.len())
218 .field("allocations", &self.allocations.len())
219 .field("largest", &allocations.as_slice())
220 .finish()
221 }
222}
223
224struct FmtBytes(u64);
225
226impl fmt::Display for FmtBytes {
227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228 const SUFFIX: [&str; 5] = ["B", "KB", "MB", "GB", "TB"];
229 let mut idx = 0;
230 let mut amount = self.0 as f64;
231 loop {
232 if amount < 1024.0 || idx == SUFFIX.len() - 1 {
233 return write!(f, "{:.2} {}", amount, SUFFIX[idx]);
234 }
235
236 amount /= 1024.0;
237 idx += 1;
238 }
239 }
240}