Skip to main content

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}