diff --git a/apps/lightcraft-cli/tests/cli.rs b/apps/lightcraft-cli/tests/cli.rs index 8f56bafe6..6bdf9e7b9 100644 --- a/apps/lightcraft-cli/tests/cli.rs +++ b/apps/lightcraft-cli/tests/cli.rs @@ -404,7 +404,12 @@ fn snapshot_defaults_to_cpu_without_gpu_environment_overrides() { let script = tmp("default-cpu.jsonl"); std::fs::write( &script, - format!("{}\n{}\n", json!({"method": "engine.execute", "params": {"command": "app.gpu"}}), json!({"method": "ui.screenshot"})), + format!( + "{}\n{}\n{}\n", + json!({"method": "engine.execute", "params": {"command": "app.gpu", "params": {"wait": false}}}), + json!({"method": "engine.execute", "params": {"command": "app.gpu"}}), + json!({"method": "ui.screenshot"}) + ), ) .unwrap(); let image = tmp("default-cpu.png"); @@ -418,11 +423,13 @@ fn snapshot_defaults_to_cpu_without_gpu_environment_overrides() { assert!(output.status.success(), "status {}: {}", output.status, String::from_utf8_lossy(&output.stderr)); let replies: Vec = String::from_utf8_lossy(&output.stdout).lines().map(|line| serde_json::from_str(line).unwrap()).collect(); assert!(replies.iter().all(|reply| reply["ok"] == true), "{replies:?}"); - let gpu = &replies[0]["result"]; - assert_eq!(gpu["enabled"], false, "{gpu}"); - assert_eq!(gpu["available"], false, "{gpu}"); - assert_eq!(gpu["adapter"], Value::Null, "no device is created: {gpu}"); - assert!(gpu["reason"].as_str().is_some_and(|reason| reason.contains("preference")), "{gpu}"); + // `wait: false` (what the UI sends) still says why the GPU is off: no device is being created + for gpu in [&replies[0]["result"], &replies[1]["result"]] { + assert_eq!(gpu["enabled"], false, "{gpu}"); + assert_eq!(gpu["available"], false, "{gpu}"); + assert_eq!(gpu["adapter"], Value::Null, "no device is created: {gpu}"); + assert!(gpu["reason"].as_str().is_some_and(|reason| reason.contains("preference")), "{gpu}"); + } let decoded = lightcraft_codecs::decode(&std::fs::read(image).unwrap(), Default::default()).unwrap(); assert_eq!((decoded.width, decoded.height), (480, 320)); } diff --git a/crates/engine/src/cmd/query.rs b/crates/engine/src/cmd/query.rs index fd81047cd..52dca09ed 100644 --- a/crates/engine/src/cmd/query.rs +++ b/crates/engine/src/cmd/query.rs @@ -226,18 +226,21 @@ pub fn specs() -> Vec { "versions": ph.versions.iter().map(|v| json!({"name": v.name, "created": v.created})).collect::>(), })) }), - cmd!(query "app.gpu", "GPU Rendering", [], None, "{enabled?: bool, wait?: bool} — allow/forbid GPU rendering (CPU fallback; LIGHTCRAFT_GPU=0 forbids it for the process); returns {enabled, available, adapter, reason (why the GPU is off), lastFallback (latest render redone on the CPU, and why)}. wait: false never waits for a device still being created (available, adapter and reason are then null)", always, |_, p| { + cmd!(query "app.gpu", "GPU Rendering", [], None, "{enabled?: bool, wait?: bool} — allow/forbid GPU rendering (CPU fallback; LIGHTCRAFT_GPU=0 forbids it for the process); returns {enabled, available, adapter, reason (why the GPU is off), lastFallback (latest render redone on the CPU, and why)}. wait: false never waits for a device still being created (available, adapter and reason are then null; with GPU rendering off they say so at once)", always, |_, p| { if let Some(on) = p.get("enabled").and_then(Value::as_bool) { lightcraft_gpu::set_enabled(on); } // the UI applies the setting on its first frame: it must not wait seconds for the // device there (issue #136; `warm_up` creates it off the UI thread) if p.get("wait").and_then(Value::as_bool) == Some(false) && !lightcraft_gpu::ready() { + // off (preference, environment): known at once, and asking the adapter would + // create the device; on: still being created, so nothing is known yet + let on = lightcraft_gpu::enabled(); return Ok(json!({ - "enabled": lightcraft_gpu::enabled(), - "available": null, + "enabled": on, + "available": if on { Value::Null } else { Value::Bool(false) }, "adapter": null, - "reason": null, + "reason": if on { None } else { lightcraft_gpu::unavailable_reason() }, "lastFallback": lightcraft_gpu::last_fallback(), })); } diff --git a/crates/ui-egui/src/panels/settings.rs b/crates/ui-egui/src/panels/settings.rs index 2f844f4ae..8debaeeaf 100644 --- a/crates/ui-egui/src/panels/settings.rs +++ b/crates/ui-egui/src/panels/settings.rs @@ -417,7 +417,10 @@ fn performance_tab(app: &mut LightcraftApp, ui: &mut egui::Ui, t: &Tokens) { use lightcraft_engine::gpu; heading(ui, t, crate::i18n::tr("Rendering")); check(ui, "settings.gpu", &mut app.ui.settings.gpu, "Use the GPU for rendering"); - let status = if !gpu::available() { + // `available` would wait for a device still being created (issue #136) + let status = if gpu::enabled() && !gpu::ready() { + "GPU: starting…".to_string() + } else if !gpu::available() { match gpu::unavailable_reason() { Some(why) => crate::i18n::tr_format!("Rendering on the CPU: {why}", why = why), None => "No usable GPU found: rendering on the CPU".to_string(),