Skip to main content

euv_ui/hook/profiler/
fn.rs

1use super::*;
2
3/// Returns the current wall-clock time, in milliseconds.
4///
5/// Browser-only wrapper around [`js_sys::Date::now`] (also doubles
6/// as `performance.now()`-based input if the host environment
7/// exposes one). Returns a `f64` because the upstream JavaScript
8/// value is also `f64` and rounding to integer milliseconds throws
9/// away the sub-millisecond precision the profiler relies on.
10///
11/// The function is `pub(crate)` because the only intended consumer
12/// lives in the same crate (the profiler's `measure` / `begin` /
13/// `end` paths); downstream code does not need to call it directly.
14///
15/// # Returns
16///
17/// - `f64` - The current wall-clock time, in milliseconds.
18pub fn now_ms() -> f64 {
19    js_sys::Date::now()
20}
21
22/// Obtains a `ProfilerHandle` registered against the current hook context slot.
23///
24/// Behaves like `HookContext::use_hook`: the same `ProfilerHandle`
25/// is returned on every render at the same hook index, so
26/// measurements pushed onto its entries signal remain visible
27/// across renders.
28///
29/// Use [`ProfilerHandle::measure`] for a single-shot
30/// "label + closure" form or [`ProfilerHandle::begin`] /
31/// [`ProfilerHandle::end`] for the split-timer form. Reads of
32/// [`ProfilerHandle::entries`] inside a render closure subscribe
33/// the render to new entries.
34///
35/// # Returns
36///
37/// - `ProfilerHandle` - The profiler handle.
38///   Returns the factory result directly when no hook context is
39///   active (e.g. when called outside a render cycle).
40pub fn use_profiler() -> ProfilerHandle {
41    HookContext::use_hook(ProfilerHandle::new_with_empty_entries)
42}
43
44/// Runs `body` and records the elapsed time under `label`.
45///
46/// Convenience helper that pairs with `App::use_interval`-style
47/// re-renders: any render closure can call `profiler_measure(label, ...)`
48/// on its hot path and the result lands in the same `ProfilerHandle`'s
49/// entries signal.
50///
51/// # Arguments
52///
53/// - `&str` - The free-form label that identifies this measurement.
54/// - `F: FnOnce() -> R` - The closure whose execution time is
55///   measured.
56///
57/// # Returns
58///
59/// - `R` - The closure's return value, unchanged.
60pub fn profiler_measure<F, R>(label: &str, body: F) -> R
61where
62    F: FnOnce() -> R,
63{
64    let profiler: ProfilerHandle = use_profiler();
65    let start_ms: f64 = now_ms();
66    let result: R = body();
67    let elapsed_ms: f64 = now_ms() - start_ms;
68    let timestamp_ms: f64 = now_ms();
69    let entry: ProfileEntry = ProfileEntry {
70        label: label.to_string(),
71        elapsed_ms,
72        timestamp_ms,
73    };
74    let next_entries: Vec<ProfileEntry> = {
75        let mut next: Vec<ProfileEntry> = profiler.get_entries().get();
76        next.push(entry);
77        next
78    };
79    profiler.get_entries().set(next_entries);
80    result
81}