ifc_lite_geometry/router/item_dedup_cache.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//! [`ItemDedupCache`] and its backing [`ItemDedupCacheState`].
6//!
7//! Split out of `router/mod.rs` (module-size ratchet) when #4083's
8//! `diagnostic_claimed` companion set gave the type its own doc-worthy shape.
9
10use crate::Mesh;
11use rustc_hash::{FxHashMap, FxHashSet};
12use std::sync::{Arc, Mutex};
13
14/// Shared content-dedup cache: maps a 128-bit structural item hash to the
15/// LOCAL (pre-placement, void-free, colour-free) item mesh PLUS its precomputed
16/// instancing `rep_identity` (`Some` when instancing tagged it, else `None`).
17/// Storing the rep beside the mesh lets a cache hit stamp it without re-running
18/// the O(verts) `compute_mesh_hash_full` per occurrence. Build ONE per loaded
19/// model with [`crate::GeometryRouter::new_dedup_cache`] and inject it into
20/// every per-element / per-batch router via
21/// [`crate::GeometryRouter::enable_content_dedup_shared`] so byte-identical
22/// geometry is meshed once regardless of how the work is partitioned across
23/// threads/batches.
24///
25/// Also carries `diagnostic_claimed` (#4083, double-count half only — see
26/// [`ItemDedupCacheState::claim_diagnostic`]): a SEPARATE small key set, not a
27/// diagnostic payload attached to `meshes`. It never changes what a mesh-cache
28/// HIT returns (still no diagnostic — that omission half of #4083 is
29/// untouched) and never gates mesh computation; it only lets two racing
30/// routers that both MISS the (structurally identical) mesh cache agree on
31/// which ONE of them keeps the CSG diagnostic it independently recorded for
32/// that miss, so one logical operation is not counted twice.
33pub type ItemDedupCache = Arc<ItemDedupCacheState>;
34
35/// Backing state behind [`ItemDedupCache`]. Opaque outside this crate: every
36/// field is private, and the only way to obtain or share one is
37/// [`crate::GeometryRouter::new_dedup_cache`] /
38/// [`crate::GeometryRouter::enable_content_dedup_shared`], so widening this
39/// struct is not a public-API break for callers that follow that contract
40/// (the only ones the type alias itself does not already lock into a
41/// concrete map/lock shape).
42#[derive(Default)]
43pub struct ItemDedupCacheState {
44 pub(super) meshes: Mutex<FxHashMap<u128, Arc<(Mesh, Option<u128>)>>>,
45 /// item_dedup_keys for which some router sharing this cache has already
46 /// recorded a CSG diagnostic this pass. See [`Self::claim_diagnostic`].
47 diagnostic_claimed: Mutex<FxHashSet<u128>>,
48}
49
50impl ItemDedupCacheState {
51 /// First caller for a given `key` wins (returns `true`, meaning: keep the
52 /// diagnostic you just recorded); every later caller for the SAME key
53 /// loses (returns `false`, meaning: discard it — another racing router
54 /// already owns this logical operation's diagnostic). Never unclaims, so
55 /// this is a per-pass, per-key latch, not a ref count.
56 pub(super) fn claim_diagnostic(&self, key: u128) -> bool {
57 self.diagnostic_claimed
58 .lock()
59 .unwrap_or_else(|e| e.into_inner())
60 .insert(key)
61 }
62}