mirror of
https://github.com/storytold/lightcraft.git
synced 2026-10-14 19:27:01 -04:00
Issue #136: Windows: use DX12 by default, honour WGPU_BACKEND, never let GPU init block startup
wgpu loads the driver of every backend in an instance's set while it enumerates adapters. The compute device (crates/gpu) hard-coded Backends::PRIMARY (Vulkan + DX12 + Metal) and ignored WGPU_BACKEND, and was created from LightcraftApp::new on a background thread at launch, so a crashing Vulkan driver (Intel UHD 630, igvk64.dll) killed the process before the window appeared; the window (egui-wgpu) also listed Vulkan. - lightcraft_gpu::backend: DX12 only on Windows (Metal on macOS, as before on Linux) for both the compute device and the window; LIGHTCRAFT_GPU_BACKEND=dx12|vulkan|metal|gl|auto|off, else WGPU_BACKEND; backends missing from the build fall back to the default. - The device is created only when GPU rendering is enabled, after the first frame; the preference is applied before the window opens. - Crash sentinel: gpu-init.marker in the settings folder around device creation; if left behind, the next launch starts with GPU rendering off and a notice saying how to turn it back on. - Tests: backend parsing/selection, sentinel, window defaults, headless snapshot with LIGHTCRAFT_GPU=0 / LIGHTCRAFT_GPU_BACKEND=off. - Docs: gpu-pipeline.md (backends, env vars, troubleshooting), whats-new, parity LR-BEHAV-GPU. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
fc3fa1a71b
commit
b66cdb23a3
9 files changed
+448
-16
No files matched your search
@@ -183,6 +183,42 @@ fn snapshot_subcommand_renders_the_ui_headlessly() {
|
||||
assert_eq!((d.width, d.height), (640, 480));
|
||||
}
|
||||
|
||||
/// Issue #136: with the GPU switched off from the environment the UI starts and renders on the CPU,
|
||||
/// without creating a GPU device (no driver is loaded), and says why.
|
||||
#[test]
|
||||
fn snapshot_starts_without_a_gpu() {
|
||||
let script = tmp("nogpu.jsonl");
|
||||
std::fs::write(
|
||||
&script,
|
||||
format!(
|
||||
"{}\n{}\n{}\n",
|
||||
json!({"method": "ui.set", "params": {"view": "photoGrid"}}),
|
||||
json!({"method": "engine.execute", "params": {"command": "app.gpu"}}),
|
||||
json!({"method": "ui.screenshot"}),
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
for (var, value, reason) in [("LIGHTCRAFT_GPU", "0", "LIGHTCRAFT_GPU=0"), ("LIGHTCRAFT_GPU_BACKEND", "off", "LIGHTCRAFT_GPU_BACKEND=off")] {
|
||||
let out = tmp(&format!("nogpu-{var}.png"));
|
||||
let o = Command::new(BIN)
|
||||
.env_remove("LIGHTCRAFT_GPU")
|
||||
.env_remove("LIGHTCRAFT_GPU_BACKEND")
|
||||
.env(var, value)
|
||||
.args(["snapshot", "--demo", "--script", script.to_str().unwrap(), "-o", out.to_str().unwrap(), "--size", "480x320"])
|
||||
.output()
|
||||
.unwrap();
|
||||
assert!(o.status.success(), "{var}: {}", String::from_utf8_lossy(&o.stderr));
|
||||
let replies: Vec<Value> = String::from_utf8_lossy(&o.stdout).lines().map(|l| serde_json::from_str(l).unwrap()).collect();
|
||||
assert!(replies.iter().all(|r| r["ok"] == true), "{replies:?}");
|
||||
let gpu = &replies[1]["result"];
|
||||
assert_eq!(gpu["available"], false, "{var}: {gpu}");
|
||||
assert_eq!(gpu["adapter"], Value::Null, "{var}: no device was created: {gpu}");
|
||||
assert!(gpu["reason"].as_str().is_some_and(|r| r.contains(reason)), "{var}: {gpu}");
|
||||
let d = lightcraft_codecs::decode(&std::fs::read(&out).unwrap(), Default::default()).unwrap();
|
||||
assert_eq!((d.width, d.height), (480, 320));
|
||||
}
|
||||
}
|
||||
|
||||
fn run_cli(args: &[&str], stdin: Option<&str>) -> (bool, Vec<Value>, String) {
|
||||
let mut child = Command::new(BIN).arg("run").args(args).stdin(Stdio::piped()).stdout(Stdio::piped()).stderr(Stdio::piped()).spawn().unwrap();
|
||||
if let Some(text) = stdin {
|
||||
|
||||
Reference in new issue
Block a user