facett-core 0.1.19

facett — visual kernel: render a node/edge Scene into egui (wgpu fast path to come)
Documentation
//! **`facett-core::render`** — the L0 shared render kernel both map skins and
//! `facett-graphview` draw through (the CONS-CORE seam).
//!
//! Phase A landed the pure additions ([`camera`], [`layer`], the moved scissor, the
//! GPU scaffold). **Phase B** adds the net-new L0 SDF kernel + the backend-swap
//! seam:
//!
//! - [`prim`] — the SDF instance vocabulary
//!   ([`QuadInstance`](prim::QuadInstance)/[`LineInstance`](prim::LineInstance) +
//!   the `Circle`/`Ring`/`Marker` constructors), `bytemuck`-able for the GPU path.
//! - [`cpu`] — the CPU lane: the SDF coverage math ([`cpu::sdf`]) + [`CpuCanvas`]
//!   rasterizing onto a `vello_cpu` pixmap, plus the moved rect scissor.
//! - [`gpu`] (feature `wgpu`) — the [`GpuSdfRenderer`](gpu::sdf_pipeline::GpuSdfRenderer):
//!   instanced quads with a fragment-shader SDF (circle/ring/marker) + thick AA
//!   instanced lines, on the Phase-A install lifecycle + bytemuck plumbing.
//! - [`backend`] — the [`decide`](backend::decide)`(probe) -> `[`Backend`] policy
//!   (lifted from graphview).
//! - [`Canvas`] / [`Renderer`] — the backend-swap seam traits. Both
//!   [`CpuRenderer`](cpu::CpuRenderer) and (feature `wgpu`)
//!   [`GpuSdfRenderer`](gpu::sdf_pipeline::GpuSdfRenderer) satisfy them.

/// **GPU ADAPTER SELECTION** — the pure, feature-free policy that decides which
/// physical device facett renders on (discrete GPU first; ASPEED/Matrox BMC
/// management displays and llvmpipe/lavapipe software rasterisers LAST), plus the
/// `FACETT_GPU_ADAPTER` override and the `state_json`-publishable decision record.
/// The wgpu glue lives in `gpu::adapter_wgpu`. See [`adapter`].
pub mod adapter;
pub mod backend;
pub mod camera;
pub mod cpu;
/// The **"cyber-toon" decoration kernel** — domain-agnostic, pure-CPU L0 beauty
/// helpers (glow halos, AO drop-shadows, animated dash-array strokes, particle
/// tracers, shockwave rings, CPU bloom, gradient-by-scalar). Zero GPU/vello, so
/// they light up identically native AND on wasm; all animation is injected-clock
/// driven so snapshots stay deterministic. See [`decor`].
pub mod decor;
/// **THE GPU TURN** — one wgpu device bring-up at a time, per process AND per box.
/// The `hold()` half is not feature-gated (a mutex and an `flock`, no wgpu types), so a
/// test file that opens a device on a feature-less build can still take it; `gputurn::probe`
/// is the `wgpu`-gated ONE WRITER for the bring-up itself, which also names the adapter it
/// got and refuses a software one. See the module doc for the five measured crashes and
/// the 13-minute deadlock that moved this here from `facett-map/tests/common/one_gpu.rs`.
pub mod gputurn;
/// **GPU LANE ATTRIBUTION** — the pure, feature-free registry that answers the
/// *other* half of the GPU question [`adapter`] cannot: is a facett GPU lane
/// actually painting, or is the app silently on the CPU painter? Folds to
/// [`lane::GpuLaneUse`] (`in_use` / `installed_unused` / `not_installed` /
/// **`unknown`**), and carries the whole story — adapter + lane — as
/// [`lane::GpuStatus`] for an About card, a status bar and a robot to share. See
/// [`lane`].
pub mod lane;
pub mod layer;
pub mod prim;
/// **THE unified view core (GFX_V2 item 6)** — a 2D map IS a 3D view under
/// constraints (tilt 0, bearing 0, orthographic, `Z(x,y) ≡ 0`, post-FX bypassed).
/// One projection writer for `facett-map` and `facett-map3d`, so an AA / miter /
/// typography / precision fix lands in both modes at once. See [`view`].
pub mod view;

/// **THE shader text** — every `.wgsl` source string, `include_str!`ed exactly once,
/// plus the `wgsl` prelude composer and the shader-CONTRACT guards. NOT behind
/// feature `wgpu`, on purpose: text needs no adapter, and gating it is what kept
/// `cull_shader_keeps_its_per_way_contract` from ever running on a default build.
/// See [`wgsl`].
pub mod wgsl;

#[cfg(feature = "wgpu")]
pub mod gpu;

/// The **L1 vello beauty overlay** (feature `l1-vello`) — gradients, antialiased
/// curves, gaussian blur / effects composited on top of the L0 SDF base. Desktop
/// GPU only; never required (L0 stands alone). See [`l1`].
#[cfg(feature = "l1-vello")]
pub mod l1;

pub use adapter::{
    adapter_score, choose_adapter_index, choose_adapter_index_with_override, gpu_unavailable,
    gpu_unavailable_json, is_management_display, is_software_rasteriser, rank_adapters,
    record_gpu_unavailable, record_selection, selected_adapter, selected_adapter_json, AdapterFacts,
    AdapterKind, AdapterSelection, GpuUnavailable,
};
pub use backend::{decide, Backend, GpuProbe};
pub use camera::{Camera, InputFeel};
pub use layer::{Layer, LayerKind, LayerStack};
pub use prim::{CircleInstance, LineInstance, MarkerInstance, QuadInstance, RingInstance};
pub use view::{GroundFrame2d, PostFx, Projection, Terrain, View};

/// A rendered frame: straight (un-premultiplied) RGBA8, row-major, `width × height`.
/// The CPU lane composites into this; the GPU lane reads it back for a parity test.
/// Mirrors graphview's `Rendered` so a skin can move over without a shape change.
#[derive(Clone, Debug, PartialEq)]
pub struct Frame {
    pub width: u32,
    pub height: u32,
    /// `width * height * 4` bytes, `[r, g, b, a]` per pixel, **not** premultiplied.
    pub rgba: Vec<u8>,
}

impl Frame {
    /// Count of pixels whose alpha is non-zero — the "did it actually draw"
    /// oracle the raster emit checkpoint reports (`raster.lit_px > 0`).
    pub fn lit_px(&self) -> usize {
        self.rgba.chunks_exact(4).filter(|p| p[3] != 0).count()
    }
}

/// **The drawing surface** — the galileo-style backend-swap seam. A host pushes
/// SDF instance batches; the concrete canvas (CPU pixmap or wgpu callback) decides
/// how they become pixels. Domain-agnostic: a map point, a graph node, and a POI
/// are all just quads; a road and an edge are all lines.
pub trait Canvas {
    /// Append a batch of SDF quad instances (circles / rings / markers, already
    /// lowered via [`QuadInstance`]). Zero-copy slice — the canvas decides whether
    /// it copies into a vertex buffer (GPU) or rasters in place (CPU).
    fn push_quads(&mut self, quads: &[QuadInstance]);
    /// Append a batch of thick-AA line instances.
    fn push_lines(&mut self, lines: &[LineInstance]);
    /// The camera this canvas draws under (the shared L0 [`Camera`]).
    fn camera(&self) -> &Camera;
}

/// **The renderer** — opens a [`Canvas`] for a frame, then presents it. The
/// 2-category swap (CPU / CPU+GPU) is expressed entirely as which `Renderer` the
/// host holds; the draw code is written once against [`Canvas`].
pub trait Renderer {
    /// Begin a frame of `width × height` pixels under `camera`, returning the
    /// [`Canvas`] to push instances into.
    fn begin(&mut self, width: u32, height: u32, camera: Camera) -> &mut dyn Canvas;
    /// Finish the frame and produce the rasterized [`Frame`] (straight RGBA8).
    fn present(&mut self) -> Frame;
    /// Which [`Backend`] this renderer is.
    fn backend(&self) -> Backend;
}

pub use cpu::{CpuCanvas, CpuRenderer};