facett-graphview 0.1.7

facett — a vello-backed, domain-agnostic scalable 2D graph render engine. Runtime-selects vello (GPU/wgpu) when a usable GPU exists, vello_cpu (multithreaded SIMD) as the no-GPU fallback. The eventual home for every graph surface (dep/arch/release dashboards, korp, graph-DB browsing).
Documentation
//! The **GPU rasterizer seam** (`vello` on wgpu) — SCAFFOLDED, not yet wired to a
//! live device. This is the fast path for large graphs: the *same* scene the CPU
//! path builds, submitted to vello's GPU compute pipeline on a wgpu 29 device
//! (exactly the wgpu the egui 0.34 stack already pins — so there is no version
//! war with eframe/egui-wgpu when this lands inside a facett host).
//!
//! Why scaffolded and not live in this spike: a real GPU render needs a wgpu
//! `Device`/`Queue` + a `vello::Renderer` + a render target texture, and a
//! headless CI box has no usable adapter — so forcing it would make the spike's
//! own tests flaky. The seam below is the exact shape the live path takes; the
//! CPU path ([`crate::cpu`]) is the proven reference. See the `gpu` cargo
//! feature and `.nornir/design.md`.

#![cfg(feature = "gpu")]

use crate::model::{Camera, Decorations, GraphModel};

/// Probe wgpu for a usable GPU adapter — the input to [`crate::backend::decide`].
/// Returns `true` if a wgpu device can actually be created (not just an adapter
/// enumerated). Cheap to call once at host start-up.
pub fn probe_usable_gpu() -> bool {
    let instance = wgpu::Instance::default();
    let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
        power_preference: wgpu::PowerPreference::HighPerformance,
        force_fallback_adapter: false,
        compatible_surface: None,
    }));
    match adapter {
        Ok(adapter) => pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
            label: Some("facett-graphview probe"),
            required_features: wgpu::Features::empty(),
            required_limits: wgpu::Limits::downlevel_defaults(),
            memory_hints: wgpu::MemoryHints::Performance,
            experimental_features: wgpu::ExperimentalFeatures::default(),
            trace: wgpu::Trace::Off,
        }))
        .is_ok(),
        Err(_) => false,
    }
}

/// Build a [`vello::Scene`] from the graph — the GPU twin of [`crate::cpu`]'s
/// scene-build step. Geometry is identical (rounded-rect chips + cubic edges);
/// only the submit target differs. STUB: fills the scene with chips so the seam
/// type-checks against vello 0.9; edge cubics + decorations are the TODO that the
/// full engine (#17) completes.
pub fn build_scene(model: &GraphModel, _decorations: &Decorations, camera: &Camera) -> vello::Scene {
    use vello::kurbo::{Affine, RoundedRect};
    use vello::peniko::{Color, Fill};

    let mut scene = vello::Scene::new();
    let hw = (crate::model::BOX_W * 0.5 * camera.zoom) as f64;
    let hh = (crate::model::BOX_H * 0.5 * camera.zoom) as f64;
    for n in &model.nodes {
        let (cx, cy) = camera.project(n.pos);
        let rect =
            RoundedRect::new(cx as f64 - hw, cy as f64 - hh, cx as f64 + hw, cy as f64 + hh, 5.0);
        let c = Color::from_rgba8(n.fill.r, n.fill.g, n.fill.b, n.fill.a);
        scene.fill(Fill::NonZero, Affine::IDENTITY, c, None, &rect);
    }
    // TODO(#17): edges (cubics + arrowheads), rings, badges, dashing — share the
    // geometry builder with `cpu.rs` so both backends paint pixel-identically.
    scene
}

// TODO(#17): a `GpuRenderer { device, queue, renderer, target }` that calls
// `vello::Renderer::render_to_texture` and reads back / blits into an egui
// texture via an `egui_wgpu` paint callback. Versions: vello 0.9 ↔ wgpu 29 ↔
// egui-wgpu 0.34 — already mutually compatible in this workspace's lock.