From 83bb74d21ce516d895e0ec50a8b9682b8556825e Mon Sep 17 00:00:00 2001 From: Jason Luker <176962611+ZJHeepfixer@users.noreply.github.com> Date: Sun, 25 Oct 2026 03:29:25 -0700 Subject: [PATCH] =?UTF-8?q?UI:=20"Preparing=20the=20GPU=20compositor?= =?UTF-8?q?=E2=80=A6"=20stays=20until=20the=20compositor=20is=20attached?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The status line is the toast that clears itself after 5 s, so the preparing notice went blank while the viewer was still on the CPU for the rest of a cold-cache build (tens of seconds), though `init_gpu` says the status bar tells the user so. draw_toast now leaves that one status up while `gpu_initializing()`; `finish_gpu_build` clears it when the compositor lands, and the worker's repaint brings that frame, so the toast doesn't tick meanwhile. A click still dismisses it; every other status clears as before. Test: with the build held on its gate, 30 more steps (7.5 s) leave the status as it was; before this change it was empty by then. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/app/frame.rs | 7 +++++++ crates/ui-egui/tests/it/ui_gpu.rs | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/crates/ui-egui/src/app/frame.rs b/crates/ui-egui/src/app/frame.rs index 6c6c737c0..057c5ce22 100644 --- a/crates/ui-egui/src/app/frame.rs +++ b/crates/ui-egui/src/app/frame.rs @@ -461,6 +461,13 @@ impl EffectcraftApp { self.ui.status.clear(); self.toast = None; } + // "Preparing the GPU compositor…" stays until the compositor is attached (tens of + // seconds on a cold shader cache), not 5 s: `finish_gpu_build` clears it, and the + // worker's repaint brings that frame, so nothing needs to tick meanwhile. A click + // still dismisses it. + if self.ui.status == crate::GPU_PREPARING && self.gpu_initializing() { + return; + } ui.ctx().request_repaint_after(std::time::Duration::from_millis(500)); if !self.ui.status.is_empty() { let since: f64 = ui.data(|d| d.get_temp(egui::Id::unique("status-since")).unwrap_or(now)); diff --git a/crates/ui-egui/tests/it/ui_gpu.rs b/crates/ui-egui/tests/it/ui_gpu.rs index c5f7084a1..e74d0ebd5 100644 --- a/crates/ui-egui/tests/it/ui_gpu.rs +++ b/crates/ui-egui/tests/it/ui_gpu.rs @@ -47,6 +47,9 @@ fn the_interface_draws_and_composites_on_the_cpu_while_the_gpu_compositor_is_bui let s = small_session(); let mut h = Harness::builder().with_size(egui::vec2(1200.0, 800.0)).wgpu().build_eframe(move |_| { let mut app = EffectcraftApp::new(s); + // The status line's 5 s expiry reads the app clock: a manual one makes the steps below + // stand for a known span of time. + app.use_manual_clock(); app.hooks.before_gpu_build = Some(Box::new(move || { let _ = gate.recv(); })); @@ -67,6 +70,14 @@ fn the_interface_draws_and_composites_on_the_cpu_while_the_gpu_compositor_is_bui assert_eq!(h.state().session.accel_note.as_deref(), Some(effectcraft_ui_egui::GPU_PREPARING)); assert!(h.state().frames.last_ms.lock().map(|v| *v > 0.0).unwrap_or(false), "a frame was composited on the CPU meanwhile"); assert!(h.state_mut().viewer_pixels().is_some(), "the viewer shows that frame"); + // Other statuses clear after 5 s; this one stays for as long as the build takes (each step + // is a quarter of a second). + for _ in 0..30 { + h.state_mut().clock_mut().advance(0.25); + h.step(); + } + assert!(h.state().gpu_initializing()); + assert_eq!(h.state().ui.status, effectcraft_ui_egui::GPU_PREPARING, "the status line still says so 7.5 s later"); release.send(()).unwrap(); settle(&mut h); assert!(!h.state().gpu_initializing());