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