mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 19:06:56 -06:00
Squash of PR #652 by @aldozar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
91 lines
3.9 KiB
Rust
91 lines
3.9 KiB
Rust
//! A wgpu error or device loss no longer closes the app (#602): it is recorded, unsaved drawings
|
|
//! are autosaved at once instead of SAVETIME minutes later, and a notice says what happened.
|
|
//! SAVEFILEPATH is process-wide, so everything runs in one test.
|
|
|
|
use cadcraft_engine::Session;
|
|
use cadcraft_engine::cmd::file::{IoHooks, set_io};
|
|
use cadcraft_engine::doc::Drawing;
|
|
use cadcraft_ui_egui::gpu::GpuFaults;
|
|
use cadcraft_ui_egui::{CadApp, Services};
|
|
use egui_wgpu::wgpu::DeviceLostReason;
|
|
|
|
/// The text the notice paints (the second frame: a new window sizes itself in the first).
|
|
fn painted_text(app: &mut CadApp, ctx: &egui::Context) -> String {
|
|
fn collect(shape: &egui::Shape, text: &mut String) {
|
|
match shape {
|
|
egui::Shape::Text(t) => {
|
|
text.push_str(t.galley.text());
|
|
text.push('\n');
|
|
}
|
|
egui::Shape::Vec(v) => v.iter().for_each(|s| collect(s, text)),
|
|
_ => {}
|
|
}
|
|
}
|
|
let mut text = String::new();
|
|
for _ in 0..2 {
|
|
let mut out = ctx.run_ui(egui::RawInput::default(), |ui| cadcraft_ui_egui::autosave::gpu_notice(app, ui.ctx()));
|
|
out.textures_delta.clear(); // no renderer here
|
|
text.clear();
|
|
out.shapes.iter().for_each(|s| collect(&s.shape, &mut text));
|
|
}
|
|
text
|
|
}
|
|
|
|
#[test]
|
|
fn faults_are_recorded_autosaved_and_shown() {
|
|
// The registry reports each new batch once; dropping the device on purpose is not a fault.
|
|
let f = GpuFaults::default();
|
|
assert_eq!(f.take(), None);
|
|
f.error("first".into());
|
|
f.error("second".into());
|
|
let got = f.take().unwrap();
|
|
assert_eq!((got.errors, got.lost, got.message.as_str()), (2, false, "second"));
|
|
assert_eq!(f.take(), None);
|
|
f.device_lost(DeviceLostReason::Destroyed, "dropped".into());
|
|
assert_eq!(f.take(), None);
|
|
f.device_lost(DeviceLostReason::Unknown, "driver reset".into());
|
|
assert!(f.take().is_some_and(|g| g.lost && g.message == "driver reset"));
|
|
|
|
set_io(IoHooks { read: |_, _| Ok(Drawing::new_metric()), write: |_, _| Ok(b"0\nEOF\n".to_vec()), plot: None });
|
|
let dir = std::env::temp_dir().join(format!("cadcraft-gpu-faults-{}", std::process::id()));
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
let mut app = CadApp::new(Session::new(), Services::default());
|
|
app.enable_autosave(&dir);
|
|
app.run("circle", serde_json::json!({"center": [0, 0], "radius": 1})).unwrap();
|
|
let ctx = egui::Context::default();
|
|
|
|
app.gpu_fault_tick(&ctx);
|
|
assert!(app.gpu_notice.is_none(), "no fault, no notice");
|
|
|
|
app.gpu_faults.error("Buffer size 302884104 is greater than the maximum buffer size".into());
|
|
app.gpu_fault_tick(&ctx);
|
|
let n = app.gpu_notice.clone().unwrap();
|
|
assert_eq!((n.fault.errors, n.fault.lost, n.autosaved), (1, false, 1));
|
|
// The change was autosaved now, not SAVETIME minutes from now.
|
|
let written = || std::fs::read_dir(&dir).map(|d| d.flatten().filter(|e| e.path().extension().is_some_and(|x| x == "sv$")).count()).unwrap_or(0);
|
|
for _ in 0..500 {
|
|
if written() == 1 {
|
|
break;
|
|
}
|
|
std::thread::sleep(std::time::Duration::from_millis(10));
|
|
}
|
|
assert_eq!(written(), 1);
|
|
let text = painted_text(&mut app, &ctx);
|
|
assert!(text.contains("graphics driver reported an error") && text.contains("recovery copy"), "{text}");
|
|
|
|
// The same error again (an invalid buffer used every frame): counted, nothing new to save.
|
|
app.gpu_faults.error("again".into());
|
|
app.gpu_fault_tick(&ctx);
|
|
let n = app.gpu_notice.clone().unwrap();
|
|
assert_eq!((n.fault.errors, n.autosaved), (2, 1));
|
|
assert!(painted_text(&mut app, &ctx).contains("(2 times)"));
|
|
|
|
app.gpu_faults.device_lost(DeviceLostReason::Unknown, "lost".into());
|
|
app.gpu_fault_tick(&ctx);
|
|
assert!(app.gpu_notice.as_ref().is_some_and(|n| n.fault.lost));
|
|
assert!(painted_text(&mut app, &ctx).contains("can't draw again until it restarts"));
|
|
|
|
app.shutdown_autosave();
|
|
let _ = std::fs::remove_dir_all(&dir);
|
|
}
|