Skip to main content

mtl_gpu/mtl4/
counters.rs

1//! MTL4 Counters implementation.
2//!
3//! Corresponds to `Metal/MTL4Counters.hpp`.
4
5use std::ffi::c_void;
6use std::ptr::NonNull;
7
8use mtl_foundation::{Integer, Referencing, UInteger};
9use mtl_sys::{msg_send_0, msg_send_1, sel};
10
11// ============================================================
12// Enums
13// ============================================================
14
15/// Counter heap type.
16///
17/// C++ equivalent: `MTL4::CounterHeapType`
18#[repr(transparent)]
19#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
20pub struct CounterHeapType(pub Integer);
21
22impl CounterHeapType {
23    /// Invalid counter heap type.
24    pub const INVALID: Self = Self(0);
25
26    /// Timestamp counter heap.
27    pub const TIMESTAMP: Self = Self(1);
28}
29
30/// Timestamp granularity.
31///
32/// C++ equivalent: `MTL4::TimestampGranularity`
33#[repr(transparent)]
34#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
35pub struct TimestampGranularity(pub Integer);
36
37impl TimestampGranularity {
38    /// Relaxed timestamp granularity.
39    pub const RELAXED: Self = Self(0);
40
41    /// Precise timestamp granularity.
42    pub const PRECISE: Self = Self(1);
43}
44
45// ============================================================
46// TimestampHeapEntry
47// ============================================================
48
49/// A timestamp entry in a counter heap.
50///
51/// C++ equivalent: `MTL4::TimestampHeapEntry`
52#[repr(C, packed)]
53#[derive(Copy, Clone, Debug, Default)]
54pub struct TimestampHeapEntry {
55    /// The timestamp value.
56    pub timestamp: u64,
57}
58
59// ============================================================
60// CounterHeapDescriptor
61// ============================================================
62
63/// Descriptor for creating a counter heap.
64///
65/// C++ equivalent: `MTL4::CounterHeapDescriptor`
66#[repr(transparent)]
67pub struct CounterHeapDescriptor(NonNull<c_void>);
68
69impl CounterHeapDescriptor {
70    /// Create a CounterHeapDescriptor from a raw pointer.
71    #[inline]
72    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
73        NonNull::new(ptr).map(Self)
74    }
75
76    /// Get the raw pointer.
77    #[inline]
78    pub fn as_raw(&self) -> *mut c_void {
79        self.0.as_ptr()
80    }
81
82    /// Create a new counter heap descriptor.
83    pub fn new() -> Option<Self> {
84        unsafe {
85            let class = mtl_sys::Class::get("MTL4CounterHeapDescriptor")?;
86            let ptr: *mut c_void = msg_send_0(class.as_ptr(), sel!(alloc));
87            if ptr.is_null() {
88                return None;
89            }
90            let ptr: *mut c_void = msg_send_0(ptr, sel!(init));
91            Self::from_raw(ptr)
92        }
93    }
94
95    /// Get the counter heap type.
96    ///
97    /// C++ equivalent: `CounterHeapType type() const`
98    pub fn heap_type(&self) -> CounterHeapType {
99        unsafe { msg_send_0(self.as_ptr(), sel!(type)) }
100    }
101
102    /// Set the counter heap type.
103    ///
104    /// C++ equivalent: `void setType(MTL4::CounterHeapType)`
105    pub fn set_heap_type(&self, heap_type: CounterHeapType) {
106        unsafe {
107            let _: () = msg_send_1(self.as_ptr(), sel!(setType:), heap_type);
108        }
109    }
110
111    /// Get the counter count.
112    ///
113    /// C++ equivalent: `NS::UInteger count() const`
114    pub fn count(&self) -> UInteger {
115        unsafe { msg_send_0(self.as_ptr(), sel!(count)) }
116    }
117
118    /// Set the counter count.
119    ///
120    /// C++ equivalent: `void setCount(NS::UInteger)`
121    pub fn set_count(&self, count: UInteger) {
122        unsafe {
123            let _: () = msg_send_1(self.as_ptr(), sel!(setCount:), count);
124        }
125    }
126}
127
128impl Default for CounterHeapDescriptor {
129    fn default() -> Self {
130        Self::new().expect("Failed to create MTL4CounterHeapDescriptor")
131    }
132}
133
134impl Clone for CounterHeapDescriptor {
135    fn clone(&self) -> Self {
136        unsafe {
137            mtl_sys::msg_send_0::<*mut c_void>(self.as_ptr(), mtl_sys::sel!(retain));
138        }
139        Self(self.0)
140    }
141}
142
143impl Drop for CounterHeapDescriptor {
144    fn drop(&mut self) {
145        unsafe {
146            mtl_sys::msg_send_0::<()>(self.as_ptr(), mtl_sys::sel!(release));
147        }
148    }
149}
150
151impl Referencing for CounterHeapDescriptor {
152    #[inline]
153    fn as_ptr(&self) -> *const c_void {
154        self.0.as_ptr()
155    }
156}
157
158unsafe impl Send for CounterHeapDescriptor {}
159unsafe impl Sync for CounterHeapDescriptor {}
160
161impl std::fmt::Debug for CounterHeapDescriptor {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        f.debug_struct("CounterHeapDescriptor")
164            .field("type", &self.heap_type())
165            .field("count", &self.count())
166            .finish()
167    }
168}
169
170// ============================================================
171// CounterHeap
172// ============================================================
173
174/// A heap for GPU performance counters.
175///
176/// C++ equivalent: `MTL4::CounterHeap`
177///
178/// CounterHeap provides storage for GPU performance counters and timestamps.
179#[repr(transparent)]
180pub struct CounterHeap(NonNull<c_void>);
181
182impl CounterHeap {
183    /// Create a CounterHeap from a raw pointer.
184    #[inline]
185    pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
186        NonNull::new(ptr).map(Self)
187    }
188
189    /// Get the raw pointer.
190    #[inline]
191    pub fn as_raw(&self) -> *mut c_void {
192        self.0.as_ptr()
193    }
194
195    /// Get the label.
196    ///
197    /// C++ equivalent: `NS::String* label() const`
198    pub fn label(&self) -> Option<String> {
199        unsafe {
200            let ns_string: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
201            if ns_string.is_null() {
202                return None;
203            }
204            let c_str: *const i8 = msg_send_0(ns_string, sel!(UTF8String));
205            if c_str.is_null() {
206                return None;
207            }
208            Some(
209                std::ffi::CStr::from_ptr(c_str)
210                    .to_string_lossy()
211                    .into_owned(),
212            )
213        }
214    }
215
216    /// Set the label.
217    ///
218    /// C++ equivalent: `void setLabel(const NS::String*)`
219    pub fn set_label(&self, label: &str) {
220        if let Some(ns_label) = mtl_foundation::String::from_str(label) {
221            unsafe {
222                let _: () = msg_send_1(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
223            }
224        }
225    }
226
227    /// Get the counter heap type.
228    ///
229    /// C++ equivalent: `CounterHeapType type() const`
230    pub fn heap_type(&self) -> CounterHeapType {
231        unsafe { msg_send_0(self.as_ptr(), sel!(type)) }
232    }
233
234    /// Get the counter count.
235    ///
236    /// C++ equivalent: `NS::UInteger count() const`
237    pub fn count(&self) -> UInteger {
238        unsafe { msg_send_0(self.as_ptr(), sel!(count)) }
239    }
240
241    /// Invalidate a range of counters.
242    ///
243    /// C++ equivalent: `void invalidateCounterRange(NS::Range)`
244    pub fn invalidate_counter_range(&self, location: UInteger, length: UInteger) {
245        unsafe {
246            let range = (location, length);
247            let _: () = msg_send_1(self.as_ptr(), sel!(invalidateCounterRange:), range);
248        }
249    }
250
251    /// Resolve a range of counters and return the data.
252    ///
253    /// C++ equivalent: `NS::Data* resolveCounterRange(NS::Range)`
254    ///
255    /// Returns the raw data pointer. Caller is responsible for interpreting
256    /// the data based on the counter heap type.
257    pub fn resolve_counter_range_raw(&self, location: UInteger, length: UInteger) -> *mut c_void {
258        unsafe {
259            let range = (location, length);
260            msg_send_1(self.as_ptr(), sel!(resolveCounterRange:), range)
261        }
262    }
263}
264
265impl Clone for CounterHeap {
266    fn clone(&self) -> Self {
267        unsafe {
268            mtl_sys::msg_send_0::<*mut c_void>(self.as_ptr(), mtl_sys::sel!(retain));
269        }
270        Self(self.0)
271    }
272}
273
274impl Drop for CounterHeap {
275    fn drop(&mut self) {
276        unsafe {
277            mtl_sys::msg_send_0::<()>(self.as_ptr(), mtl_sys::sel!(release));
278        }
279    }
280}
281
282impl Referencing for CounterHeap {
283    #[inline]
284    fn as_ptr(&self) -> *const c_void {
285        self.0.as_ptr()
286    }
287}
288
289unsafe impl Send for CounterHeap {}
290unsafe impl Sync for CounterHeap {}
291
292impl std::fmt::Debug for CounterHeap {
293    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
294        f.debug_struct("CounterHeap")
295            .field("label", &self.label())
296            .field("type", &self.heap_type())
297            .field("count", &self.count())
298            .finish()
299    }
300}
301
302#[cfg(test)]
303mod tests {
304    use super::*;
305
306    #[test]
307    fn test_counter_heap_descriptor_size() {
308        assert_eq!(
309            std::mem::size_of::<CounterHeapDescriptor>(),
310            std::mem::size_of::<*mut c_void>()
311        );
312    }
313
314    #[test]
315    fn test_counter_heap_size() {
316        assert_eq!(
317            std::mem::size_of::<CounterHeap>(),
318            std::mem::size_of::<*mut c_void>()
319        );
320    }
321
322    #[test]
323    fn test_timestamp_heap_entry_size() {
324        assert_eq!(std::mem::size_of::<TimestampHeapEntry>(), 8);
325    }
326
327    #[test]
328    fn test_counter_heap_type_values() {
329        assert_eq!(CounterHeapType::INVALID.0, 0);
330        assert_eq!(CounterHeapType::TIMESTAMP.0, 1);
331    }
332
333    #[test]
334    fn test_timestamp_granularity_values() {
335        assert_eq!(TimestampGranularity::RELAXED.0, 0);
336        assert_eq!(TimestampGranularity::PRECISE.0, 1);
337    }
338}