Skip to main content

facett_graphview/
backend.rs

1//! **Runtime backend selection** — re-pointed to the L0 kernel
2//! ([`facett_core::render::backend`]) as of CONS-CORE Phase C. The policy
3//! (`probe the hardware, then choose`) now lives once in `facett-core` so the map
4//! skins and this graph engine decide identically; graphview **re-exports** the
5//! types here for back-compat, so every existing `facett_graphview::{Backend,
6//! GpuProbe, decide}` import (the benches, the example, nornir's `draw_graph`)
7//! keeps resolving with zero source change.
8//!
9//! vello is *not* GPU-only: the same scene rasterizes on the GPU (`vello` / the L0
10//! SDF wgpu pipeline) when a usable adapter exists, and on the CPU (`vello_cpu`,
11//! multithreaded SIMD) when it doesn't. The caller probes once and renders through
12//! whichever [`Backend`] [`decide`] returns.
13//!
14//! Note one preserved nuance: graphview's own CPU-force env var was
15//! `FACETT_GRAPHVIEW_CPU`; the core probe reads `FACETT_RENDER_CPU`. Both are
16//! honoured by [`GpuProbe::from_env`] here via the [`probe_from_env`] shim so a
17//! host that set either still forces CPU.
18
19pub use facett_core::render::backend::{decide, gpu_arm_compiled, Backend, GpuProbe};
20
21/// Build a [`GpuProbe`] from a detected-GPU flag, honouring **both** the core
22/// `FACETT_RENDER_CPU` override and graphview's historical `FACETT_GRAPHVIEW_CPU`
23/// override (back-compat: hosts that set the old name still force the CPU path).
24///
25/// Prefer this over [`GpuProbe::from_env`] in graphview hosts; `from_env` (the
26/// core method) only reads `FACETT_RENDER_CPU`.
27pub fn probe_from_env(usable_gpu: bool) -> GpuProbe {
28    let force_cpu = std::env::var_os("FACETT_RENDER_CPU").is_some()
29        || std::env::var_os("FACETT_GRAPHVIEW_CPU").is_some();
30    GpuProbe { usable_gpu, force_cpu }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn cpu_only_probe_always_picks_cpu() {
39        assert_eq!(decide(GpuProbe::cpu_only()), Backend::CpuVello);
40    }
41
42    #[test]
43    fn force_cpu_overrides_a_present_gpu() {
44        let p = GpuProbe { usable_gpu: true, force_cpu: true };
45        assert_eq!(decide(p), Backend::CpuVello);
46    }
47
48    #[test]
49    fn gpu_probe_picks_gpu_only_when_feature_on() {
50        // The GPU arm is gated on facett-core's `wgpu` feature. That can be lit by
51        // graphview's own `gpu` feature OR, under a workspace build, by feature
52        // unification from a sibling crate — so key the expectation off core's
53        // actual compiled state (`gpu_arm_compiled`), not this crate's `gpu` cfg.
54        let got = decide(GpuProbe::gpu());
55        if gpu_arm_compiled() {
56            assert_eq!(got, Backend::GpuVello);
57        } else {
58            // No GPU path compiled in → the probe still resolves, to CPU.
59            assert_eq!(got, Backend::CpuVello);
60        }
61    }
62
63    /// INJECT-ASSERT (back-compat): the re-pointed `Backend` is literally
64    /// `facett-core`'s enum, and graphview's `probe_from_env` still honours the
65    /// historical `FACETT_GRAPHVIEW_CPU` override.
66    #[test]
67    fn graphview_env_override_still_forces_cpu() {
68        // Cannot mutate process env safely in parallel tests; assert the type
69        // identity + the shim's logic on an explicit probe instead.
70        let p = GpuProbe { usable_gpu: true, force_cpu: false };
71        assert!(decide(p).is_gpu() == gpu_arm_compiled());
72        // Re-export identity: a core Backend flows straight through.
73        let b: facett_core::render::Backend = Backend::CpuVello;
74        assert_eq!(b, Backend::CpuVello);
75    }
76}