ifc_lite_wasm/api/pipeline_diagnostics.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! WASM API: getPipelineDiagnostics — structured per-load pipeline
6//! diagnostics (the `PipelineDiagnostics` contract from
7//! `ifc_lite_processing::pipeline_diagnostics`).
8//!
9//! Collected on the NORMAL load path: every `processGeometryBatch*` call
10//! folds one batch record into the per-worker accumulator (cheap counters
11//! plus two `js_sys::Date::now()` reads, so it is always on — no feature
12//! flag). `std::time::Instant` traps on wasm32, hence the JS clock; the
13//! scan/prepass phases run in JS workers outside this module and report 0
14//! here (the native server/CLI surface those numbers through ProcessingStats +
15//! GeometryDiagnostics, not through this channel). Crossed to JS via
16//! `serde_wasm_bindgen::to_value`, exactly like `diagnoseGeometry`.
17
18use super::IfcAPI;
19use wasm_bindgen::prelude::*;
20
21#[wasm_bindgen]
22impl IfcAPI {
23 /// Structured pipeline diagnostics accumulated across every
24 /// `processGeometryBatch*` call since the last load reset
25 /// (`clearPrePassCache` / `setEntityIndex`), as a JS object with a
26 /// `schemaVersion` field — or `undefined` when no batch has run yet.
27 /// Includes per-batch summed geometry wall time, mesh/triangle counts,
28 /// the degenerate-backstop drop count, and the CSG failure aggregates.
29 #[wasm_bindgen(js_name = getPipelineDiagnostics)]
30 pub fn get_pipeline_diagnostics(&self) -> JsValue {
31 let diag = self
32 .pipeline_diagnostics
33 .lock()
34 .unwrap_or_else(std::sync::PoisonError::into_inner);
35 if diag.is_empty() {
36 return JsValue::UNDEFINED;
37 }
38 serde_wasm_bindgen::to_value(&*diag).unwrap_or(JsValue::UNDEFINED)
39 }
40}
41
42impl IfcAPI {
43 /// Fold one produced batch into the per-worker accumulator. Called by
44 /// `produce_batch` at the end of every batch; not exposed to JS.
45 #[allow(clippy::too_many_arguments)]
46 pub(crate) fn record_pipeline_batch(
47 &self,
48 element_count: u64,
49 mesh_count: u64,
50 triangle_count: u64,
51 backstop_count: u64,
52 point_cache_hits: u64,
53 point_cache_misses: u64,
54 geometry_ms: u64,
55 diag: &ifc_lite_geometry::GeometryDiagnostics,
56 ) {
57 let mut acc = self
58 .pipeline_diagnostics
59 .lock()
60 .unwrap_or_else(std::sync::PoisonError::into_inner);
61 // Faceted-brep wall time is native/observability-only (Instant traps on
62 // wasm32), so the wasm batch reports 0 for it.
63 acc.record_batch(
64 element_count,
65 mesh_count,
66 triangle_count,
67 backstop_count,
68 point_cache_hits,
69 point_cache_misses,
70 0,
71 geometry_ms,
72 diag,
73 );
74 }
75
76 /// Reset the accumulator (a new load on a reused IfcAPI). Called from
77 /// `clearPrePassCache` and `setEntityIndex`.
78 pub(crate) fn reset_pipeline_diagnostics(&self) {
79 let mut acc = self
80 .pipeline_diagnostics
81 .lock()
82 .unwrap_or_else(std::sync::PoisonError::into_inner);
83 *acc = ifc_lite_processing::PipelineDiagnostics::default();
84 }
85}