graph-explorer-render 0.3.0

WebGL2/wgpu renderer for graph-explorer — nodes, edges, halos and labels.
Documentation
//! Parse and type-check every bundled WGSL shader.
//!
//! wgpu compiles WGSL when `create_shader_module` runs, which on this crate's
//! only real target means "inside a browser, on a canvas, after a wasm build".
//! A shader typo therefore used to surface as a blank canvas and a console
//! error, with `cargo build` perfectly green. Running naga's front end over the
//! same source files here turns that into an ordinary test failure.
//!
//! This validates the shaders in isolation — it cannot check that a shader's
//! vertex inputs line up with the `vertex_attr_array!` layout its pipeline
//! declares, since that pairing only exists at pipeline-creation time.

use naga::valid::{Capabilities, ValidationFlags, Validator};

/// Every shader, paired with the path it lives at so a failure names the file.
const SHADERS: &[(&str, &str)] = &[
    ("shaders/edge.wgsl", include_str!("../src/shaders/edge.wgsl")),
    ("shaders/node.wgsl", include_str!("../src/shaders/node.wgsl")),
    ("shaders/halo.wgsl", include_str!("../src/shaders/halo.wgsl")),
];

#[test]
fn every_shader_parses_and_validates() {
    for (path, source) in SHADERS {
        let module = naga::front::wgsl::parse_str(source)
            .unwrap_or_else(|e| panic!("{path} failed to parse:\n{}", e.emit_to_string(source)));
        // The browser target is WebGL2, which grants no optional capabilities;
        // validating against the empty set is what catches a shader that would
        // compile on a native desktop backend but fail in the browser.
        Validator::new(ValidationFlags::all(), Capabilities::empty())
            .validate(&module)
            .unwrap_or_else(|e| panic!("{path} failed to validate: {e:?}"));
    }
}

/// Both antialiased pipelines must agree on how far they grow their geometry,
/// because the two are drawn against each other constantly: an edge meets a
/// node rim on nearly every link in the graph. A mismatch would leave a seam
/// or an overlap along that join at some zoom levels.
#[test]
fn edge_and_node_use_the_same_aa_pad() {
    let pad_of = |src: &str| {
        src.lines()
            .find_map(|l| l.trim().strip_prefix("const AA_PAD: f32 = ")?.strip_suffix(';').map(str::to_owned))
            .expect("AA_PAD is declared as a top-level const")
    };
    assert_eq!(
        pad_of(include_str!("../src/shaders/edge.wgsl")),
        pad_of(include_str!("../src/shaders/node.wgsl")),
    );
}