wgpu_types/
counters.rs

1#[cfg(feature = "counters")]
2use std::sync::atomic::{AtomicIsize, Ordering};
3use std::{fmt, ops::Range};
4
5/// An internal counter for debugging purposes
6///
7/// Internally represented as an atomic isize if the `counters` feature is enabled,
8/// or compiles to nothing otherwise.
9pub struct InternalCounter {
10    #[cfg(feature = "counters")]
11    value: AtomicIsize,
12}
13
14impl InternalCounter {
15    /// Creates a counter with value 0.
16    #[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    /// Get the counter's value.
26    #[cfg(feature = "counters")]
27    #[inline]
28    pub fn read(&self) -> isize {
29        self.value.load(Ordering::Relaxed)
30    }
31
32    /// Get the counter's value.
33    ///
34    /// Always returns 0 if the `counters` feature is not enabled.
35    #[cfg(not(feature = "counters"))]
36    #[inline]
37    #[must_use]
38    pub fn read(&self) -> isize {
39        0
40    }
41
42    /// Get and reset the counter's value.
43    ///
44    /// Always returns 0 if the `counters` feature is not enabled.
45    #[cfg(feature = "counters")]
46    #[inline]
47    pub fn take(&self) -> isize {
48        self.value.swap(0, Ordering::Relaxed)
49    }
50
51    /// Get and reset the counter's value.
52    ///
53    /// Always returns 0 if the `counters` feature is not enabled.
54    #[cfg(not(feature = "counters"))]
55    #[inline]
56    #[must_use]
57    pub fn take(&self) -> isize {
58        0
59    }
60
61    /// Increment the counter by the provided amount.
62    #[inline]
63    pub fn add(&self, _val: isize) {
64        #[cfg(feature = "counters")]
65        self.value.fetch_add(_val, Ordering::Relaxed);
66    }
67
68    /// Decrement the counter by the provided amount.
69    #[inline]
70    pub fn sub(&self, _val: isize) {
71        #[cfg(feature = "counters")]
72        self.value.fetch_add(-_val, Ordering::Relaxed);
73    }
74
75    /// Sets the counter to the provided value.
76    #[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/// `wgpu-hal`'s internal counters.
105#[allow(missing_docs)]
106#[derive(Clone, Default)]
107pub struct HalCounters {
108    // API objects
109    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    // Resources
124    /// Amount of allocated gpu memory attributed to buffers, in bytes.
125    pub buffer_memory: InternalCounter,
126    /// Amount of allocated gpu memory attributed to textures, in bytes.
127    pub texture_memory: InternalCounter,
128    /// Number of gpu memory allocations.
129    pub memory_allocations: InternalCounter,
130}
131
132/// `wgpu-core`'s internal counters.
133#[derive(Clone, Default)]
134pub struct CoreCounters {
135    // TODO    #[cfg(features=)]
136}
137
138/// All internal counters, exposed for debugging purposes.
139#[derive(Clone, Default)]
140pub struct InternalCounters {
141    /// `wgpu-core` counters.
142    pub core: CoreCounters,
143    /// `wgpu-hal` counters.
144    pub hal: HalCounters,
145}
146
147/// Describes an allocation in the [`AllocatorReport`].
148#[derive(Clone)]
149pub struct AllocationReport {
150    /// The name provided to the `allocate()` function.
151    pub name: String,
152    /// The offset in bytes of the allocation in its memory block.
153    pub offset: u64,
154    /// The size in bytes of the allocation.
155    pub size: u64,
156}
157
158/// Describes a memory block in the [`AllocatorReport`].
159#[derive(Clone)]
160pub struct MemoryBlockReport {
161    /// The size in bytes of this memory block.
162    pub size: u64,
163    /// The range of allocations in [`AllocatorReport::allocations`] that are associated
164    /// to this memory block.
165    pub allocations: Range<usize>,
166}
167
168/// A report that can be generated for informational purposes using `Allocator::generate_report()`.
169#[derive(Clone)]
170pub struct AllocatorReport {
171    /// All live allocations, sub-allocated from memory blocks.
172    pub allocations: Vec<AllocationReport>,
173    /// All memory blocks.
174    pub blocks: Vec<MemoryBlockReport>,
175    /// Sum of the memory used by all allocations, in bytes.
176    pub total_allocated_bytes: u64,
177    /// Sum of the memory reserved by all memory blocks including unallocated regions, in bytes.
178    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}