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

nordisk/facett

facett-core/tests/gpu_install_checked.rs

//! **`install_renderer_checked` gives a refused install an owner** (needs `--features wgpu`).
//!
//! `install_renderer` opens no error scope, so a pipeline its `make` builds invalid goes
//! to the device's uncaptured-error handler — on native wgpu that is a panic, in the
//! browser a console line and a pane that draws nothing — and nothing in facett's state
//! says which lane did it. The checked variant pushes a VALIDATION scope around the
//! install, pops it WITHOUT blocking (native: one poll; wasm32: `spawn_local`), and
//! reports a captured error through `lane::note_gpu_error`, undoing the install.
//!
//! Both directions are pinned against a real `egui_wgpu::RenderState`, each with its own
//! renderer TYPE (the lane registry is keyed by `type_name`, and is process-global):
//!
//! * a renderer whose pipeline names a vertex entry point the shader does not have is
//!   REFUSED — said in the lane's idle reason and in `gpu_error()`, gone from
//!   `callback_resources`, `lane::is_installed` false, `false` returned;
//! * a valid renderer is installed and reports NOTHING — no idle reason, flag up.
//!
//! RED: replace the body of `install_renderer_checked` with a plain `install_renderer`
//! call and the first test fails — the error never reaches `note_gpu_error` (it goes to
//! the uncaptured-error handler instead, which in wgpu's default panics).
//!
//! Run: `cargo test -p facett-core --features wgpu --test gpu_install_checked`
#![cfg(feature = "wgpu")]

use egui_wgpu::wgpu;
use facett_core::render::gpu::{install_renderer_checked, msaa_state};
use facett_core::render::lane;

/// A real `egui_wgpu::RenderState`, no window (see `tests/gpu_pick_host.rs`).
fn render_state() -> egui_wgpu::RenderState {
    let setup = egui_kittest::wgpu::default_wgpu_setup();
    egui_kittest::wgpu::create_render_state(setup, Default::default())
}

const WGSL: &str = r"
@vertex
fn vs_main(@builtin(vertex_index) i: u32) -> @builtin(position) vec4<f32> {
    let x = f32(i & 1u) * 2.0 - 1.0;
    let y = f32((i >> 1u) & 1u) * 2.0 - 1.0;
    return vec4<f32>(x, y, 0.0, 1.0);
}

@fragment
fn fs_main() -> @location(0) vec4<f32> {
    return vec4<f32>(1.0, 0.5, 0.25, 1.0);
}
";

/// One pipeline over [`WGSL`], with `vertex_entry` as its vertex entry point.
fn pipeline(device: &wgpu::Device, format: wgpu::TextureFormat, vertex_entry: &str) -> wgpu::RenderPipeline {
    let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
        label: Some("install_checked_probe"),
        source: wgpu::ShaderSource::Wgsl(WGSL.into()),
    });
    device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
        label: Some("install_checked_probe"),
        layout: None,
        vertex: wgpu::VertexState {
            module: &shader,
            entry_point: Some(vertex_entry),
            compilation_options: Default::default(),
            buffers: &[],
        },
        primitive: wgpu::PrimitiveState::default(),
        depth_stencil: None,
        multisample: msaa_state(),
        fragment: Some(wgpu::FragmentState {
            module: &shader,
            entry_point: Some("fs_main"),
            compilation_options: Default::default(),
            targets: &[Some(wgpu::ColorTargetState {
                format,
                blend: None,
                write_mask: wgpu::ColorWrites::ALL,
            })],
        }),
        multiview_mask: None,
        cache: None,
    })
}

/// A renderer the device accepts.
struct ValidRenderer {
    _pipeline: wgpu::RenderPipeline,
}

/// A renderer whose pipeline names a vertex entry point [`WGSL`] does not have.
struct InvalidRenderer {
    _pipeline: wgpu::RenderPipeline,
}

#[test]
fn an_invalid_pipeline_is_reported_through_note_gpu_error_and_the_install_undone() {
    let _gpu = facett_core::render::gputurn::hold();
    let rs = render_state();
    let name = std::any::type_name::<InvalidRenderer>();

    let installed = install_renderer_checked(&rs, |device, format| InvalidRenderer {
        _pipeline: pipeline(device, format, "no_such_entry_point"),
    });

    let why = lane::idle_reason_of(name)
        .expect("the refusal must be said in the lane's own row (note_gpu_error sets it)");
    assert!(
        why.contains("InvalidRenderer") && why.contains("install refused"),
        "the reason names the lane and what happened: {why}",
    );
    assert!(
        lane::gpu_error().is_some(),
        "note_gpu_error must also publish the process's gpu_error (state_json `gpu_error`)",
    );
    assert!(!installed, "a refused install must not report itself as installed");
    assert!(
        rs.renderer.read().callback_resources.get::<InvalidRenderer>().is_none(),
        "the refused renderer must leave callback_resources — a paint callback would \
         otherwise draw through a pipeline the device rejected",
    );
    assert!(!lane::is_installed(name), "and the cheap read must send the pane to its CPU painter");
    assert!(
        lane::lanes().iter().any(|l| l.name == name && !l.installed),
        "the record says the same",
    );
}

#[test]
fn a_valid_pipeline_installs_and_reports_nothing() {
    let _gpu = facett_core::render::gputurn::hold();
    let rs = render_state();
    let name = std::any::type_name::<ValidRenderer>();

    let installed = install_renderer_checked(&rs, |device, format| ValidRenderer {
        _pipeline: pipeline(device, format, "vs_main"),
    });

    assert!(installed, "a fresh RenderState must accept a valid renderer");
    assert_eq!(lane::idle_reason_of(name), None, "a valid install must report nothing");
    assert!(lane::is_installed(name));
    assert!(rs.renderer.read().callback_resources.get::<ValidRenderer>().is_some());

    // Install-once: the second call opens a scope over nothing and changes nothing.
    let again = install_renderer_checked(&rs, |device, format| ValidRenderer {
        _pipeline: pipeline(device, format, "vs_main"),
    });
    assert!(!again, "already installed → false, `make` not run");
    assert_eq!(lane::idle_reason_of(name), None);
    assert!(lane::is_installed(name));
}