use std::cell::Cell;
use web_time::Instant;
#[derive(Default, Clone, Copy)]
pub(in crate::watertight_tessellation) struct TessProfile {
pub enabled: bool,
pub faces: u64,
pub edge_sampling_ms: f64,
pub seed_ms: f64,
pub refine_ms: f64,
pub stations: u64,
pub stations_placed: u64,
pub margin_tests: u64,
pub parity_tests: u64,
pub host_visits: u64,
pub refine_passes: u64,
pub splits: u64,
pub flip_ms: f64,
pub flip_rounds: u64,
pub flips: u64,
pub host_ms: f64,
pub boundary_cross_tests: u64,
}
thread_local! {
static PROFILE: Cell<TessProfile> = const { Cell::new(TessProfile {
enabled: false, faces: 0, edge_sampling_ms: 0.0, seed_ms: 0.0, refine_ms: 0.0,
stations: 0, stations_placed: 0, margin_tests: 0, parity_tests: 0, host_visits: 0,
refine_passes: 0, splits: 0, flip_ms: 0.0, flip_rounds: 0, flips: 0, host_ms: 0.0,
boundary_cross_tests: 0,
}) };
}
pub(in crate::watertight_tessellation) fn tess_profile(update: impl FnOnce(&mut TessProfile)) {
PROFILE.with(|cell| {
let mut profile = cell.get();
if profile.enabled {
update(&mut profile);
cell.set(profile);
}
});
}
pub(in crate::watertight_tessellation) fn tess_profile_enabled() -> bool {
PROFILE.with(|cell| cell.get().enabled)
}
pub(in crate::watertight_tessellation) fn profile_started() -> Option<Instant> {
tess_profile_enabled().then(Instant::now)
}
pub(in crate::watertight_tessellation) fn elapsed_ms(started: Option<Instant>) -> f64 {
started.map_or(0.0, |s| s.elapsed().as_secs_f64() * 1_000.0)
}
pub(in crate::watertight_tessellation) fn begin() -> Option<Instant> {
let enabled = profile_switch();
PROFILE.with(|cell| {
cell.set(TessProfile {
enabled,
..TessProfile::default()
})
});
enabled.then(Instant::now)
}
#[cfg(not(target_arch = "wasm32"))]
fn profile_switch() -> bool {
std::env::var("BREP_PROFILE").is_ok()
}
#[cfg(target_arch = "wasm32")]
fn profile_switch() -> bool {
false
}
pub(in crate::watertight_tessellation) fn end(started: Option<Instant>, chord: f64, triangles: usize) {
let Some(started) = started else {
return;
};
let p = PROFILE.with(|cell| cell.get());
eprintln!(
"tess.profile ms={:.2} chord={chord:.3e} faces={} tris={triangles} | \
edge_sampling_ms={:.2} seed_ms={:.2} refine_ms={:.2} | stations={} placed={} \
margin_tests={} parity_tests={} host_ms={:.2} host_visits={} \
flip_ms={:.2} flip_rounds={} flips={} cross_tests={} refine_passes={} splits={}",
started.elapsed().as_secs_f64() * 1_000.0,
p.faces,
p.edge_sampling_ms,
p.seed_ms,
p.refine_ms,
p.stations,
p.stations_placed,
p.margin_tests,
p.parity_tests,
p.host_ms,
p.host_visits,
p.flip_ms,
p.flip_rounds,
p.flips,
p.boundary_cross_tests,
p.refine_passes,
p.splits,
);
PROFILE.with(|cell| cell.set(TessProfile::default()));
}
#[derive(Clone, Copy)]
pub(in crate::watertight_tessellation) enum Stage {
Seed,
Refine,
Flip,
Host,
}
pub(in crate::watertight_tessellation) struct StageTimer {
started: Option<Instant>,
stage: Stage,
}
pub(in crate::watertight_tessellation) fn stage_timer(stage: Stage) -> StageTimer {
StageTimer {
started: profile_started(),
stage,
}
}
impl Drop for StageTimer {
fn drop(&mut self) {
let ms = elapsed_ms(self.started);
match self.stage {
Stage::Seed => tess_profile(|p| p.seed_ms += ms),
Stage::Refine => tess_profile(|p| p.refine_ms += ms),
Stage::Flip => tess_profile(|p| p.flip_ms += ms),
Stage::Host => tess_profile(|p| p.host_ms += ms),
}
}
}