-
Notifications
You must be signed in to change notification settings - Fork 46
Expand file tree
/
Copy pathprofile_capture.rs
More file actions
118 lines (102 loc) · 3.95 KB
/
Copy pathprofile_capture.rs
File metadata and controls
118 lines (102 loc) · 3.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! CPU profiling driver: runs a 30-second audio+video capture with the
//! realistic per-frame workload (`frame_info` on every frame, `audio_buffer_list`
//! on every audio buffer) so a sampling profiler (`samply`, `xctrace`) sees a
//! representative slice of the dispatch path.
//!
//! Build with `--release` (debug profiling is meaningless here) and the
//! release profile retains debug info for symbolication:
//!
//! ```bash
//! cargo build --release --example profile_capture --features macos_14_0
//! samply record --rate 4000 -- ./target/release/examples/profile_capture
//! ```
//!
//! Or with `xctrace`'s Time Profiler:
//!
//! ```bash
//! xctrace record --template "Time Profiler" --output ./cap.trace \
//! --launch -- ./target/release/examples/profile_capture
//! ```
//!
//! To generate a flame graph after `samply`, open the printed URL in your
//! browser (Firefox profiler) and use the inverted-call-tree view to find
//! per-callback hot self-time.
use screencapturekit::cm::{CMSampleBufferExt, CMSampleBufferSCExt};
use screencapturekit::prelude::*;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
const CAPTURE_SECONDS: u64 = 30;
extern "C" {
fn sc_initialize_core_graphics();
}
struct Counters {
video: AtomicUsize,
audio: AtomicUsize,
}
fn init_core_graphics() {
// SAFETY: This initializes the process-global Core Graphics state once
// before capture starts; the Swift bridge exposes it specifically for this
// setup step.
unsafe { sc_initialize_core_graphics() };
}
fn rate_per_second(count: usize, elapsed: Duration) -> f64 {
u32::try_from(count).map_or_else(|_| f64::from(u32::MAX), f64::from) / elapsed.as_secs_f64()
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🔎 profile_capture — driving a {CAPTURE_SECONDS}s capture window for sampling");
// Force CG init so the profile doesn't capture the first-call setup cost.
init_core_graphics();
let content = SCShareableContent::get()?;
let display = content.displays().into_iter().next().ok_or("no display")?;
let filter = SCContentFilter::create()
.with_display(&display)
.with_excluding_windows(&[])
.build();
let config = SCStreamConfiguration::new()
.with_width(1920)
.with_height(1080)
.with_pixel_format(PixelFormat::BGRA)
.with_captures_audio(true)
.with_sample_rate(48000)
.with_channel_count(2);
let counters = Arc::new(Counters {
video: AtomicUsize::new(0),
audio: AtomicUsize::new(0),
});
let mut stream = SCStream::new(&filter, &config);
let video_counters = Arc::clone(&counters);
stream.add_output_handler(
move |buf: CMSampleBuffer, _ot: SCStreamOutputType| {
// Realistic per-video-frame workload — the path that profile
// attributions should show.
let _info = buf.frame_info();
let _img = buf.image_buffer();
video_counters.video.fetch_add(1, Ordering::Relaxed);
},
SCStreamOutputType::Screen,
);
let audio_counters = Arc::clone(&counters);
stream.add_output_handler(
move |buf: CMSampleBuffer, _ot: SCStreamOutputType| {
// Realistic per-audio-buffer workload.
let _abl = buf.audio_buffer_list();
audio_counters.audio.fetch_add(1, Ordering::Relaxed);
},
SCStreamOutputType::Audio,
);
println!("Starting capture...");
let t0 = Instant::now();
stream.start_capture()?;
std::thread::sleep(Duration::from_secs(CAPTURE_SECONDS));
stream.stop_capture()?;
let elapsed = t0.elapsed();
let v = counters.video.load(Ordering::Relaxed);
let a = counters.audio.load(Ordering::Relaxed);
println!(
"✅ Done in {elapsed:?} — {v} video frames ({:.1} fps) + {a} audio buffers ({:.1} bps)",
rate_per_second(v, elapsed),
rate_per_second(a, elapsed),
);
Ok(())
}