Skip to main content

euv_ui/hook/profiler/
impl.rs

1use super::*;
2
3/// Inherent implementation of [`ProfilerHandle`].
4impl ProfilerHandle {
5    /// Records a fresh measurement around the given closure.
6    ///
7    /// Captures the start timestamp, runs `f`, captures the
8    /// end timestamp, and pushes a `ProfileEntry { label,
9    /// elapsed_ms, timestamp_ms }` into the entries signal.
10    /// The `elapsed_ms` is `>= 0.0` by construction (start is
11    /// always captured before `f` runs).
12    ///
13    /// # Arguments
14    ///
15    /// - `&str` - The label for this measurement.
16    /// - `F: FnOnce() -> R` - The closure to measure. Can
17    ///   return any type — the return value is forwarded to
18    ///   the caller unchanged.
19    ///
20    /// # Returns
21    ///
22    /// - `R` - Whatever `f` returned.
23    pub fn measure<F, R>(&self, label: &str, f: F) -> R
24    where
25        F: FnOnce() -> R,
26    {
27        let started_ms: f64 = now_ms();
28        let result: R = f();
29        let ended_ms: f64 = now_ms();
30        let entry: ProfileEntry =
31            ProfileEntry::new(label.to_string(), ended_ms - started_ms, ended_ms);
32        // Push the entry onto the existing entries vector.
33        // Read-modify-write via `.get()` is necessary because
34        // `Signal<T>::set` requires `T: Clone` (which
35        // `Vec<ProfileEntry>` is) but does NOT take `&mut T`
36        // — the entire new value is supplied as the argument.
37        let mut current: Vec<ProfileEntry> = self.get_entries().get();
38        current.push(entry);
39        self.get_entries().set(current);
40        result
41    }
42
43    /// Starts a measurement that will end later.
44    ///
45    /// Use this when the measured region is not a single
46    /// closure — e.g. you want to bracket an async operation
47    /// or a callback fired from event handling. The returned
48    /// `ProfilerMark` knows the start timestamp and the
49    /// entries signal; pass it to `end()` when the work
50    /// completes.
51    ///
52    /// # Arguments
53    ///
54    /// - `&str` - The label for this measurement.
55    ///
56    /// # Returns
57    ///
58    /// - `ProfilerMark` - An RAII-ish guard. Call `mark.end()`
59    ///   to push the `ProfileEntry`; drop without `end()` to
60    ///   discard the measurement.
61    pub fn begin(&self, label: &str) -> ProfilerMark {
62        ProfilerMark::new(label.to_string(), now_ms(), *self.get_entries())
63    }
64
65    /// Empties the entries vector. Useful between benchmarks
66    /// ("measure just this call, not the previous ones too")
67    /// and in tests ("start from a clean slate").
68    pub fn clear(&self) {
69        self.get_entries().set(Vec::new());
70    }
71}
72
73/// Inherent implementation of [`ProfilerMark`].
74impl ProfilerMark {
75    /// Closes the measurement started by `begin()` and pushes
76    /// the resulting `ProfileEntry` into the entries signal.
77    ///
78    /// After calling `end()`, the marker is consumed and
79    /// cannot be reused. Calling `end()` twice is a no-op on
80    /// the second call — the marker is moved into the first
81    /// call, so the borrow checker prevents a second
82    /// invocation in well-typed code.
83    pub fn end(self) {
84        let ended_ms: f64 = now_ms();
85        let entry: ProfileEntry = ProfileEntry::new(
86            self.get_label().clone(),
87            ended_ms - self.get_started_ms(),
88            ended_ms,
89        );
90        let mut current: Vec<ProfileEntry> = self.get_entries().get();
91        current.push(entry);
92        self.get_entries().set(current);
93    }
94}