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));
}