Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

facett-core/Cargo.toml

[package]
name = "facett-core"
description = "facett — visual kernel: render a node/edge Scene into egui (wgpu fast path to come)"
# 0.3.0 (RA2-14): the error-code link API was renamed after crates.io's 0.2.0 (the old
# names are gone: SOURCE_REPO, source_url and the JSON `source_url`), a semver break, so
# this crate leaves the workspace's 0.2 line. Its dependents ask 0.3.0.
version = "0.3.0"
edition.workspace = true
license.workspace = true
repository.workspace = true
# Development notes, agent instructions and machine-local tooling stay in the
# repository; they are not part of the published crate.
exclude = [
    ".nornir/",
]

[dependencies]
facett-source-link = { path = "../facett-source-link", version = "0.1.0" }
egui = { workspace = true, features = ["serde"] }
serde = { version = "1", features = ["derive"] }
serde_json = { workspace = true }
arrow-array = "59"
arrow-schema = "59"
# The 4×4 algebra for the UNIFIED VIEW CORE (`render::view`, GFX_V2 item 6) — the
# one projection writer `facett-map` (2D ortho) and `facett-map3d` (3D orbit) both
# route through. Same version facett-map3d already locks for its shadow/inverse
# algebra (GFX_V2 item 3), so this adds no new version to the graph. Pure Rust,
# `default-features = false` so no `rand`/`serde`/`mint` edges come with it.
glam = { version = "0.33", default-features = false, features = ["std"] }
# L0 CPU raster target (CONS-CORE Phase B). vello_cpu is the spec's CpuCanvas
# pixmap backend; it is already a transitive dep (via epaint 0.34 / facett-graphview)
# at exactly this version, so promoting it to a direct dep adds no version clash.
# Multithreaded-SIMD software rasterizer — the proof "vello" is not GPU-only.
vello_cpu = "0.0.6"
# GATLING multicore is LAW (rayon is FORBIDDEN). The CPU SDF raster fans across all
# cores via znippy's fork-join gatling (`gatling_scanlines`): no-barrier, self-
# dispatching workers claim whole scanlines and write into their row's DISJOINT
# pre-allocated slice — zero-copy (row indices, not pixel copies), zero-alloc in the
# hot loop, no wait-for-a-core (a worker grabs the next row the instant it finishes).
# This is the fork-join sibling I added to znippy-zoomies for the case where all the
# work (the H scanlines) is known upfront. See render/cpu/mod.rs.
# REGISTRY, with the sibling checkout supplied by the workspace-root
# `[patch.crates-io]` for local development. A `path` here escaped the repo, and
# a consumer fetching facett-core BY GIT (knut pins facett-pipeline to a rev so a
# bare `git clone knut` builds, 2026-09-10) failed with "no matching package
# named `znippy-zoomies` found … location searched: Git repository facett" —
# the path is relative to a checkout that has no `../../znippy-zoomies`.
znippy-zoomies = "0.1.26"
arboard = { version = "3", optional = true }
# The GOLDEN-IMAGE GUARD (`golden` feature, see src/golden.rs): PNG decode/encode so
# a screenshot proof can COMPARE against its committed golden instead of overwriting
# it. OPTIONAL + dev-only in practice — leaf crates enable it from `[dev-dependencies]`
# (`facett-core = { path = "...", features = ["golden"] }`), so a release build of the
# kernel carries no image edge at all.
image = { version = "0.25", optional = true, default-features = false, features = ["png"] }
# L4: the functional-status emitter that feeds the `nornir test` matrix. OPTIONAL
# + behind the `testmatrix` feature so a release build carries no dependency edge
# and strips every emit call. facett-core owns this edge so leaf crates only need
# a `testmatrix = ["facett-core/testmatrix"]` passthrough.
nornir-testmatrix = { version = "0.2", optional = true }

# L0 GPU lane (CONS-CORE). All optional + behind the `wgpu` feature so a default
# build carries no GPU dependency edge. Versions match the egui 0.36 stack already
# co-locked across the workspace (egui-wgpu 0.36.2 ↔ wgpu 30) — bump them together.
#
# `default-features = false` on egui-wgpu is LOAD-BEARING, and the reason is the
# WEB build. egui-wgpu's default list is:
#     fragile-send-sync-non-atomic-wasm, macos-window-resize-jitter-fix,
#     wgpu/default, wgpu/webgl
# — and that last entry compiles wgpu's WebGL2 backend (plus `glow`) into every
# wasm surface that touches facett-core's GPU lane. facett renders on WebGPU
# (`preferred_backends()` is `Backends::BROWSER_WEBGPU` on wasm32), so that backend
# is DEAD CODE the runtime mask can never reach — it cost the facet-wrapped wheel
# ~3.8MB of download for nothing. Cargo features are additive and unify across the
# graph, so a downstream crate cannot subtract `webgl`; the only place it can be
# kept out is here, where it is switched on.
#
# The two defaults that ARE wanted are re-listed explicitly:
#   * fragile-send-sync-non-atomic-wasm — lets single-threaded browser handles
#     satisfy the Send/Sync bounds egui-wgpu requires;
#   * macos-window-resize-jitter-fix — native macOS resize behaviour (→ wgpu/metal).
# `wgpu/default` is NOT re-listed because it is not egui-wgpu's to give here: the
# direct `wgpu` dependency below carries its own defaults, which is where the
# native Vulkan/Metal/DX12 backends come from. Native behaviour is unchanged.
egui-wgpu = { version = "0.36", optional = true, default-features = false, features = [
    "fragile-send-sync-non-atomic-wasm",
    "macos-window-resize-jitter-fix",
] }
wgpu = { workspace = true, optional = true }
bytemuck = { version = "1", optional = true, features = ["derive"] }
pollster = { version = "1", optional = true }
# Scene3D's text (`render::gpu::scene3d`): the SDF glyph atlas is baked from egui's own
# monospace font (Hack, the bytes epaint already ships) through ttf-parser's outlines.
# Both are already in the lock at these versions (epaint 0.36 / the font stack), so the
# graph gains edges, not versions.
epaint_default_fonts = { version = "0.36", optional = true }
ttf-parser = { version = "0.25", optional = true, default-features = false, features = ["std"] }

# L1 GPU beauty overlay (the `vello` family member that needs a *fat* GPU /
# compute). OPTIONAL + behind the `l1-vello` feature so a default (and even a
# plain `wgpu` L0) build carries no vello edge. This is the spec's L1 lane:
# antialiased curves, gradients, blur/effects — the things the L0 SDF base
# deliberately skips — composited over the L0 frame.
# vello (0.9, and 0.10 too) is on wgpu 29 while egui 0.36 is on wgpu 30, so the
# overlay opens its own headless device on `wgpu29` (the workspace alias) — it never
# shared egui's device. Drop `wgpu29` when vello ships on wgpu 30.
vello = { version = "0.9", optional = true }
wgpu29 = { workspace = true, optional = true }

[features]
# OS image clipboard bridge (CLIP-5). Off by default — in-process typed transfer
# works without it; this adds the cross-application image lane via arboard.
os-image = ["dep:arboard"]
# The golden-image guard for screenshot proofs (`facett_core::golden`). Enabled from
# the [dev-dependencies] of the crates that own a golden PNG; never in a release build.
golden = ["dep:image"]
# Install the bundled Inter UI font as the default Proportional family in
# `LookTheme::apply` (the "soft text" fix — replaces egui's built-in Ubuntu-Light).
# OFF by default and carries NO dependency: it only flips `include_bytes!` of the
# font asset ON. The asset (`facett-core/assets/fonts/Inter-Regular.ttf` — Inter,
# SIL OFL, see assets/fonts/OFL.txt) is PRESENT, so `--features bundled-fonts`
# compiles and gives crisp Inter instead of egui's Ubuntu-Light. Kept opt-in here
# so it doesn't silently re-font every facett consumer; an app enables it (or adds
# it to its own default features).
bundled-fonts = []
# Turn the `testmatrix` bridge from a compiled-out no-op into a real row-emitter
# (also turns on the upstream crate's own emitter). Leaf facett crates add a
# passthrough `testmatrix = ["facett-core/testmatrix"]`.
testmatrix = ["dep:nornir-testmatrix", "nornir-testmatrix/testmatrix"]
# L0 GPU lane: the shared SDF/instanced render scaffold (bytemuck types +
# color32_to_f32, the install/callback lifecycle, the upload-once + chunking buffer
# algorithm). OFF by default — the CPU lane needs no GPU. Skins opt in.
wgpu = ["dep:egui-wgpu", "dep:wgpu", "dep:bytemuck", "dep:pollster", "dep:wasm-bindgen-futures", "dep:epaint_default_fonts", "dep:ttf-parser"]
# Headless tests of a pane's RESIDENT cloud lane: adds
# `graphcloud::cloud::assume_renderer_installed_for_tests`, which opens the
# installed-renderer gate without a device. Only `[dev-dependencies]` turn it on.
cloud-test-gate = ["wgpu"]
# L1 vello beauty overlay (render-engine.md §"L0 / L1"): the OPTIONAL `vello` GPU
# overlay — gradients, antialiased curves, gaussian blur / effects — composited on
# top of the L0 SDF base. Pulls in `wgpu` (vello renders to a wgpu texture; it is a
# *fat-GPU* lane, desktop-only, never required). A device/tank/headless-CPU build
# leaves this OFF and L0 still looks great with zero vello. See `render/l1`.
l1-vello = ["dep:vello", "dep:wgpu29", "wgpu"]

# `flock(2)` for the CROSS-PROCESS half of `render::gputurn` — the GPU turn every facett
# crate's device tests take. Unix-only and behind `cfg(unix)`, so no other target grows an
# edge, and already in this workspace's graph (wgpu/winit pull it) so it costs no build.
# `flock` and not a lock file: the kernel releases it when the process dies, so a crashed
# test cannot leave a stale lock for every later run to trip over.
[target.'cfg(unix)'.dependencies]
libc = "0.2"

# The navgraph's browser half (`navgraph::web`): keep Chrome from opening its find bar on
# the Ctrl+G chord (eframe's web runner does not prevent the default for Ctrl+G), and read
# `?nav=labels` from the page URL. Both crates are already in every wasm graph through
# eframe, at the locked versions, so this adds no build.
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
# `render::gpu::install_renderer_checked` settles its validation error scope on the
# browser's event loop (the host installs synchronously in eframe's CreationContext and
# cannot await). Already in every wasm graph through eframe's web runner, at the locked
# version, so this adds no build; behind `wgpu` like the rest of the GPU lane.
wasm-bindgen-futures = { version = "0.4", optional = true }
web-sys = { version = "0.3", features = ["Window", "Location", "KeyboardEvent", "EventTarget", "Event"] }
# REPRODUCIBLE WASM. arrow-array 59 declares, for wasm32 ONLY and not behind a feature,
# `ahash = { default-features = false, features = ["compile-time-rng"] }`
# (arrow-array-59.2.0/Cargo.toml:170-173). compile-time-rng = `const_random!(u64)`, a
# proc macro that draws getrandom AT COMPILE TIME, so every wasm build baked 64 fresh
# random bytes into each inlined copy of ahash's seed table. MEASURED 2026-09-29 (oden):
# main's core wasm differed between two fresh target dirs in 73 × 64-byte data runs.
# A feature cannot be switched OFF from here (cargo features only add), but ahash's own
# precedence can be used (ahash-0.8.12/src/random_state.rs:47-81): with `runtime-rng`
# on, `get_fixed_seeds` reads getrandom at RUN time and the const_random! branch is not
# compiled. This line turns `runtime-rng` on by feature unification — no fork, no
# [patch] — and `getrandom/wasm_js` (0.3.4+: the feature alone selects the backend, no
# RUSTFLAGS cfg) routes it to `crypto.getRandomValues`. Result: the seeds are random per
# page load instead of per compile, and the wasm is byte-identical build to build.
ahash = { version = "0.8", default-features = false, features = ["runtime-rng"] }
# STAYS on getrandom 0.3 (2026-10-03, finish-bump; the constellation moved to 0.4):
# this line exists only to switch `wasm_js` on for ahash's OWN getrandom, and ahash
# 0.8.12 (newest) depends on getrandom 0.3. MEASURED: with this line on "0.4",
# `cargo check --target wasm32-unknown-unknown -p facett-core` fails in getrandom
# 0.3.4 ("wasm32-unknown-unknown targets are not supported by default"). Move it when
# ahash moves.
getrandom-wasm-js = { package = "getrandom", version = "0.3.4", features = ["wasm_js"] }

[dev-dependencies]
# The effects_demo example renders the live palettes + raven. WGPU backend (LAW #4:
# modern platforms only — the last `glow` dev-dep in facett went with the 2026-08-01
# purge). The effects module itself paints with layered-alpha shapes and needs no
# custom GPU shader; this is just the window backend eframe opens the demo with.
# EXAMPLES ONLY — `cargo test` builds these targets but never runs them, so nothing
# here puts a GPU requirement on a headless test.
# `wgpu_no_default_features`, NOT `wgpu` (LAW 4 / GFX_V2 Decision 0). eframe's plain
# `wgpu` feature expands to `egui-wgpu/default` -> `wgpu/default` -> `wgpu-hal/default`
# -> **glow**, the GL backend this constellation dropped. Cargo features are additive
# and unify across the whole graph, so one DEV dependency spelling it this way turns the
# GL backend on for every crate here, normal edges included. Backends come from the
# workspace `wgpu` entry.
eframe = { version = "0.36", default-features = false, features = ["default_fonts", "wgpu_no_default_features", "wayland", "x11"] }
# Interaction-layer (FC-TESTS): drive a facet headless + query the AccessKit tree
# by role/label/value. Proves the FC-4 a11y helper publishes real nodes. No wgpu
# needed — node-tree assertions don't render pixels.
# `wgpu` is on for ONE reason: `tests/gpu_pick_host.rs` needs a real
# `egui_wgpu::RenderState` (via `egui_kittest::wgpu::create_render_state`) to prove the
# pick lane's install-once lifecycle and its `pick_at` gates against the actual egui
# host object, not a hand-made Device/Queue pair. facett-map already pins this exact
# feature combination, so no new resolution enters the lock. DEV-ONLY.
egui_kittest = { version = "0.36", features = ["snapshot", "wgpu"] }
# HEAVY legibility bench (benches/heavy_legibility.rs). Same criterion major the rest
# of the workspace's benches already pin, so no second version enters the lock.
criterion = { version = "0.8", default-features = false }

[[bench]]
name = "heavy_legibility"
harness = false

[[example]]
name = "checkout_surface"
path = "examples/checkout_surface.rs"

[[example]]
name = "effects_demo"
path = "examples/effects_demo.rs"

[[example]]
name = "deck_fx_demo"
path = "examples/deck_fx_demo.rs"

# `cloud_frame_cost` measures the wgpu cloud lane against the instance lane, so it only
# exists under `wgpu`. Declared so a DEFAULT-feature `cargo build --examples` skips it
# rather than failing to compile.
[[example]]
name = "cloud_frame_cost"
required-features = ["wgpu"]