Skip to main content

wgpu_types/
counters.rs

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