1use std::ffi::c_void;
16use std::ptr::NonNull;
17
18use mtl_foundation::{Referencing, UInteger};
19use mtl_sys::{msg_send_0, msg_send_1, sel};
20
21use crate::Device;
22use crate::enums::{CounterSampleBufferError, StorageMode};
23
24#[repr(C, packed)]
32#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
33pub struct CounterResultTimestamp {
34 pub timestamp: u64,
36}
37
38#[repr(C, packed)]
42#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
43pub struct CounterResultStageUtilization {
44 pub total_cycles: u64,
46 pub vertex_cycles: u64,
48 pub tessellation_cycles: u64,
50 pub post_tessellation_vertex_cycles: u64,
52 pub fragment_cycles: u64,
54 pub render_target_cycles: u64,
56}
57
58#[repr(C, packed)]
62#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
63pub struct CounterResultStatistic {
64 pub tessellation_input_patches: u64,
66 pub vertex_invocations: u64,
68 pub post_tessellation_vertex_invocations: u64,
70 pub clipper_invocations: u64,
72 pub clipper_primitives_out: u64,
74 pub fragment_invocations: u64,
76 pub fragments_passed: u64,
78 pub compute_kernel_invocations: u64,
80}
81
82pub const COUNTER_ERROR_VALUE: UInteger = !0;
88
89pub const COUNTER_DONT_SAMPLE: UInteger = !0;
91
92#[repr(transparent)]
103pub struct Counter(NonNull<c_void>);
104
105impl Counter {
106 #[inline]
112 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
113 NonNull::new(ptr).map(Self)
114 }
115
116 #[inline]
118 pub fn as_raw(&self) -> *mut c_void {
119 self.0.as_ptr()
120 }
121
122 pub fn name(&self) -> Option<String> {
126 unsafe {
127 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(name));
128 if ptr.is_null() {
129 return None;
130 }
131 let utf8_ptr: *const std::ffi::c_char =
132 mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
133 if utf8_ptr.is_null() {
134 return None;
135 }
136 let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
137 Some(c_str.to_string_lossy().into_owned())
138 }
139 }
140}
141
142impl Clone for Counter {
143 fn clone(&self) -> Self {
144 unsafe {
145 msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
146 }
147 Self(self.0)
148 }
149}
150
151impl Drop for Counter {
152 fn drop(&mut self) {
153 unsafe {
154 msg_send_0::<()>(self.as_ptr(), sel!(release));
155 }
156 }
157}
158
159impl Referencing for Counter {
160 #[inline]
161 fn as_ptr(&self) -> *const c_void {
162 self.0.as_ptr()
163 }
164}
165
166unsafe impl Send for Counter {}
167unsafe impl Sync for Counter {}
168
169impl std::fmt::Debug for Counter {
170 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
171 f.debug_struct("Counter")
172 .field("name", &self.name())
173 .finish()
174 }
175}
176
177#[repr(transparent)]
188pub struct CounterSet(NonNull<c_void>);
189
190impl CounterSet {
191 #[inline]
197 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
198 NonNull::new(ptr).map(Self)
199 }
200
201 #[inline]
203 pub fn as_raw(&self) -> *mut c_void {
204 self.0.as_ptr()
205 }
206
207 pub fn name(&self) -> Option<String> {
211 unsafe {
212 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(name));
213 if ptr.is_null() {
214 return None;
215 }
216 let utf8_ptr: *const std::ffi::c_char =
217 mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
218 if utf8_ptr.is_null() {
219 return None;
220 }
221 let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
222 Some(c_str.to_string_lossy().into_owned())
223 }
224 }
225
226 pub fn counters_raw(&self) -> *mut c_void {
232 unsafe { msg_send_0(self.as_ptr(), sel!(counters)) }
233 }
234
235 pub fn counter_count(&self) -> UInteger {
237 unsafe {
238 let array = self.counters_raw();
239 if array.is_null() {
240 return 0;
241 }
242 msg_send_0(array as *const c_void, sel!(count))
243 }
244 }
245
246 pub fn counter_at_index(&self, index: UInteger) -> Option<Counter> {
248 unsafe {
249 let array = self.counters_raw();
250 if array.is_null() {
251 return None;
252 }
253 let ptr: *mut c_void = msg_send_1(array as *const c_void, sel!(objectAtIndex:), index);
254 if ptr.is_null() {
255 return None;
256 }
257 msg_send_0::<*mut c_void>(ptr as *const c_void, sel!(retain));
258 Counter::from_raw(ptr)
259 }
260 }
261}
262
263impl Clone for CounterSet {
264 fn clone(&self) -> Self {
265 unsafe {
266 msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
267 }
268 Self(self.0)
269 }
270}
271
272impl Drop for CounterSet {
273 fn drop(&mut self) {
274 unsafe {
275 msg_send_0::<()>(self.as_ptr(), sel!(release));
276 }
277 }
278}
279
280impl Referencing for CounterSet {
281 #[inline]
282 fn as_ptr(&self) -> *const c_void {
283 self.0.as_ptr()
284 }
285}
286
287unsafe impl Send for CounterSet {}
288unsafe impl Sync for CounterSet {}
289
290impl std::fmt::Debug for CounterSet {
291 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292 f.debug_struct("CounterSet")
293 .field("name", &self.name())
294 .field("counter_count", &self.counter_count())
295 .finish()
296 }
297}
298
299#[repr(transparent)]
307pub struct CounterSampleBufferDescriptor(NonNull<c_void>);
308
309impl CounterSampleBufferDescriptor {
310 pub fn alloc() -> Option<Self> {
314 unsafe {
315 let cls = mtl_sys::Class::get("MTLCounterSampleBufferDescriptor")?;
316 let ptr: *mut c_void = msg_send_0(cls.as_ptr(), sel!(alloc));
317 Self::from_raw(ptr)
318 }
319 }
320
321 pub fn init(&self) -> Option<Self> {
325 unsafe {
326 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(init));
327 Self::from_raw(ptr)
328 }
329 }
330
331 pub fn new() -> Option<Self> {
333 Self::alloc()?.init()
334 }
335
336 #[inline]
342 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
343 NonNull::new(ptr).map(Self)
344 }
345
346 #[inline]
348 pub fn as_raw(&self) -> *mut c_void {
349 self.0.as_ptr()
350 }
351
352 pub fn counter_set(&self) -> Option<CounterSet> {
356 unsafe {
357 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(counterSet));
358 if ptr.is_null() {
359 return None;
360 }
361 msg_send_0::<*mut c_void>(ptr as *const c_void, sel!(retain));
362 CounterSet::from_raw(ptr)
363 }
364 }
365
366 pub fn set_counter_set(&self, counter_set: &CounterSet) {
370 unsafe {
371 msg_send_1::<(), *const c_void>(
372 self.as_ptr(),
373 sel!(setCounterSet:),
374 counter_set.as_ptr(),
375 );
376 }
377 }
378
379 pub fn label(&self) -> Option<String> {
383 unsafe {
384 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
385 if ptr.is_null() {
386 return None;
387 }
388 let utf8_ptr: *const std::ffi::c_char =
389 mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
390 if utf8_ptr.is_null() {
391 return None;
392 }
393 let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
394 Some(c_str.to_string_lossy().into_owned())
395 }
396 }
397
398 pub fn set_label(&self, label: &str) {
402 if let Some(ns_label) = mtl_foundation::String::from_str(label) {
403 unsafe {
404 msg_send_1::<(), *const c_void>(self.as_ptr(), sel!(setLabel:), ns_label.as_ptr());
405 }
406 }
407 }
408
409 #[inline]
413 pub fn sample_count(&self) -> UInteger {
414 unsafe { msg_send_0(self.as_ptr(), sel!(sampleCount)) }
415 }
416
417 #[inline]
421 pub fn set_sample_count(&self, count: UInteger) {
422 unsafe {
423 msg_send_1::<(), UInteger>(self.as_ptr(), sel!(setSampleCount:), count);
424 }
425 }
426
427 #[inline]
431 pub fn storage_mode(&self) -> StorageMode {
432 unsafe { msg_send_0(self.as_ptr(), sel!(storageMode)) }
433 }
434
435 #[inline]
439 pub fn set_storage_mode(&self, mode: StorageMode) {
440 unsafe {
441 msg_send_1::<(), StorageMode>(self.as_ptr(), sel!(setStorageMode:), mode);
442 }
443 }
444}
445
446impl Default for CounterSampleBufferDescriptor {
447 fn default() -> Self {
448 Self::new().expect("failed to create CounterSampleBufferDescriptor")
449 }
450}
451
452impl Clone for CounterSampleBufferDescriptor {
453 fn clone(&self) -> Self {
454 unsafe {
455 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(copy));
456 Self::from_raw(ptr).expect("failed to copy CounterSampleBufferDescriptor")
457 }
458 }
459}
460
461impl Drop for CounterSampleBufferDescriptor {
462 fn drop(&mut self) {
463 unsafe {
464 msg_send_0::<()>(self.as_ptr(), sel!(release));
465 }
466 }
467}
468
469impl Referencing for CounterSampleBufferDescriptor {
470 #[inline]
471 fn as_ptr(&self) -> *const c_void {
472 self.0.as_ptr()
473 }
474}
475
476unsafe impl Send for CounterSampleBufferDescriptor {}
477unsafe impl Sync for CounterSampleBufferDescriptor {}
478
479impl std::fmt::Debug for CounterSampleBufferDescriptor {
480 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
481 f.debug_struct("CounterSampleBufferDescriptor")
482 .field("label", &self.label())
483 .field("sample_count", &self.sample_count())
484 .field("storage_mode", &self.storage_mode())
485 .finish()
486 }
487}
488
489#[repr(transparent)]
500pub struct CounterSampleBuffer(NonNull<c_void>);
501
502impl CounterSampleBuffer {
503 #[inline]
509 pub unsafe fn from_raw(ptr: *mut c_void) -> Option<Self> {
510 NonNull::new(ptr).map(Self)
511 }
512
513 #[inline]
515 pub fn as_raw(&self) -> *mut c_void {
516 self.0.as_ptr()
517 }
518
519 pub fn device(&self) -> Device {
523 unsafe {
524 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(device));
525 msg_send_0::<*mut c_void>(ptr as *const c_void, sel!(retain));
526 Device::from_raw(ptr).expect("counter sample buffer has no device")
527 }
528 }
529
530 pub fn label(&self) -> Option<String> {
534 unsafe {
535 let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(label));
536 if ptr.is_null() {
537 return None;
538 }
539 let utf8_ptr: *const std::ffi::c_char =
540 mtl_sys::msg_send_0(ptr as *const c_void, sel!(UTF8String));
541 if utf8_ptr.is_null() {
542 return None;
543 }
544 let c_str = std::ffi::CStr::from_ptr(utf8_ptr);
545 Some(c_str.to_string_lossy().into_owned())
546 }
547 }
548
549 #[inline]
553 pub fn sample_count(&self) -> UInteger {
554 unsafe { msg_send_0(self.as_ptr(), sel!(sampleCount)) }
555 }
556
557 pub fn resolve_counter_range_raw(
564 &self,
565 location: UInteger,
566 length: UInteger,
567 ) -> Result<*mut c_void, CounterSampleBufferError> {
568 unsafe {
569 let range = mtl_foundation::Range::new(location, length);
570 let ptr: *mut c_void = msg_send_1(self.as_ptr(), sel!(resolveCounterRange:), range);
573 if ptr.is_null() {
574 Err(CounterSampleBufferError::INVALID)
575 } else {
576 Ok(ptr)
577 }
578 }
579 }
580}
581
582impl Clone for CounterSampleBuffer {
583 fn clone(&self) -> Self {
584 unsafe {
585 msg_send_0::<*mut c_void>(self.as_ptr(), sel!(retain));
586 }
587 Self(self.0)
588 }
589}
590
591impl Drop for CounterSampleBuffer {
592 fn drop(&mut self) {
593 unsafe {
594 msg_send_0::<()>(self.as_ptr(), sel!(release));
595 }
596 }
597}
598
599impl Referencing for CounterSampleBuffer {
600 #[inline]
601 fn as_ptr(&self) -> *const c_void {
602 self.0.as_ptr()
603 }
604}
605
606unsafe impl Send for CounterSampleBuffer {}
607unsafe impl Sync for CounterSampleBuffer {}
608
609impl std::fmt::Debug for CounterSampleBuffer {
610 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
611 f.debug_struct("CounterSampleBuffer")
612 .field("label", &self.label())
613 .field("sample_count", &self.sample_count())
614 .finish()
615 }
616}
617
618#[cfg(test)]
619mod tests {
620 use super::*;
621
622 #[test]
623 fn test_counter_size() {
624 assert_eq!(
625 std::mem::size_of::<Counter>(),
626 std::mem::size_of::<*mut c_void>()
627 );
628 }
629
630 #[test]
631 fn test_counter_set_size() {
632 assert_eq!(
633 std::mem::size_of::<CounterSet>(),
634 std::mem::size_of::<*mut c_void>()
635 );
636 }
637
638 #[test]
639 fn test_counter_sample_buffer_descriptor_size() {
640 assert_eq!(
641 std::mem::size_of::<CounterSampleBufferDescriptor>(),
642 std::mem::size_of::<*mut c_void>()
643 );
644 }
645
646 #[test]
647 fn test_counter_sample_buffer_size() {
648 assert_eq!(
649 std::mem::size_of::<CounterSampleBuffer>(),
650 std::mem::size_of::<*mut c_void>()
651 );
652 }
653
654 #[test]
655 fn test_counter_result_timestamp_size() {
656 assert_eq!(std::mem::size_of::<CounterResultTimestamp>(), 8);
657 }
658
659 #[test]
660 fn test_counter_result_stage_utilization_size() {
661 assert_eq!(std::mem::size_of::<CounterResultStageUtilization>(), 48);
662 }
663
664 #[test]
665 fn test_counter_result_statistic_size() {
666 assert_eq!(std::mem::size_of::<CounterResultStatistic>(), 64);
667 }
668
669 #[test]
670 fn test_constants() {
671 assert_eq!(COUNTER_ERROR_VALUE, !0);
672 assert_eq!(COUNTER_DONT_SAMPLE, !0);
673 }
674}