mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-14 19:27:05 -04:00
Merge pull request #36 from storytold/pr/never-crash-ui
Never crash: frame-pool panic handler and ui-egui without expect()
This commit is contained in:
6 files changed
+23
-10
No files matched your search
@@ -253,7 +253,8 @@ pub struct Frames {
|
||||
/// Pending jobs, picked by priority when a pool thread frees up (the viewer's frame first).
|
||||
queue: Arc<Mutex<Queue>>,
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pool: rayon::ThreadPool,
|
||||
/// None if the OS refused the threads: jobs then go to rayon's global pool.
|
||||
pool: Option<rayon::ThreadPool>,
|
||||
ctx: Option<egui::Context>,
|
||||
/// Render time of the last viewer (urgent) frame in ms, for the Info panel / perf readout.
|
||||
pub last_ms: Arc<Mutex<f64>>,
|
||||
@@ -272,7 +273,14 @@ impl Default for Frames {
|
||||
inflight: Arc::new(Mutex::new(HashSet::new())),
|
||||
queue: Arc::new(Mutex::new(Queue::default())),
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pool: rayon::ThreadPoolBuilder::new().num_threads(threads).thread_name(|i| format!("ec-frame-{i}")).build().expect("frame pool"),
|
||||
// A panicking job would abort the whole process (rayon's default for `spawn`): log it
|
||||
// and lose only that frame (the panic hook has logged the message).
|
||||
pool: rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(threads)
|
||||
.thread_name(|i| format!("ec-frame-{i}"))
|
||||
.panic_handler(|_| log::error!("a frame render panicked; that frame was skipped"))
|
||||
.build()
|
||||
.ok(),
|
||||
ctx: None,
|
||||
last_ms: Arc::new(Mutex::new(0.0)),
|
||||
content_keys: Arc::default(),
|
||||
@@ -440,9 +448,13 @@ impl Frames {
|
||||
if self.remote.is_none() {
|
||||
let w = self.worker();
|
||||
// One spawn per job; each spawn renders whichever queued job matters most right now.
|
||||
self.pool.spawn(move || {
|
||||
let job = move || {
|
||||
w.run_next();
|
||||
});
|
||||
};
|
||||
match &self.pool {
|
||||
Some(pool) => pool.spawn(job),
|
||||
None => rayon::spawn(job),
|
||||
}
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
if let Some(ctx) = &self.ctx {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
//! Everything goes through `effectcraft_engine::Session::execute` (or `menus::invoke` for
|
||||
//! frontend-only commands) so every gesture is also available to agents.
|
||||
|
||||
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
||||
|
||||
pub mod audio;
|
||||
pub mod automation;
|
||||
pub mod bench;
|
||||
|
||||
@@ -570,8 +570,7 @@ pub fn show(app: &mut EffectcraftApp, ui: &mut egui::Ui, rect: Rect) {
|
||||
lp.text(pos2(cx_num + 2.0, row.center().y), Align2::LEFT_CENTER, n.to_string(), Tokens::ui(12.0), t.text_dim);
|
||||
let name = comp_item.map(|i| i.name.clone()).unwrap_or_else(|| "(missing composition)".into());
|
||||
lp.text(pos2(cx_name + 2.0, row.center().y), Align2::LEFT_CENTER, &name, Tokens::ui(12.0), t.text);
|
||||
let status = if rendering && progress.as_ref().and_then(|p| p.current) == Some(it.id) {
|
||||
let pr = progress.as_ref().expect("progress");
|
||||
let status = if rendering && let Some(pr) = progress.as_ref().filter(|p| p.current == Some(it.id)) {
|
||||
format!("Rendering {:.0}%", if pr.total > 0 { pr.done as f64 * 100.0 / pr.total as f64 } else { 0.0 })
|
||||
} else {
|
||||
it.status.label().to_string()
|
||||
|
||||
@@ -176,7 +176,8 @@ fn page_ui(app: &mut EffectcraftApp, ui: &mut egui::Ui, page: &Page, cur: &Value
|
||||
}
|
||||
r
|
||||
}
|
||||
Kind::Bool => unreachable!(),
|
||||
// Checkboxes are drawn above; nothing to edit here.
|
||||
Kind::Bool => ui.label(""),
|
||||
};
|
||||
resp = Some(r);
|
||||
});
|
||||
|
||||
@@ -193,8 +193,7 @@ pub fn hook(
|
||||
handled = true;
|
||||
} else if e.contains(map, pos) {
|
||||
let ci = e.hit(map, pos);
|
||||
let sel = editing.as_ref().expect("editing");
|
||||
let anchor = if mods.shift { sel.anchor } else { ci };
|
||||
let anchor = if mods.shift { editing.as_ref().map_or(ci, |sel| sel.anchor) } else { ci };
|
||||
exec(app, "text.setSelection", json!({"anchor": anchor, "caret": ci}));
|
||||
typing_key(ui, true);
|
||||
drag = Some(Drag::Select { layer: e.layer.id });
|
||||
|
||||
@@ -380,7 +380,7 @@ pub(crate) fn draw_frame(app: &mut EffectcraftApp, ctx: &egui::Context, painter:
|
||||
// read it back first (asynchronously in the browser: from the next frame on).
|
||||
let gpu = matches!((&app.viewer_shown, &app.viewer_native), (Some(s), Some(n)) if s.0 == n.0 && s.1 == n.1 && n.1.comp == cid.0);
|
||||
let rect = if gpu {
|
||||
let id = app.viewer_shown.map(|s| s.0).expect("shown");
|
||||
let Some(id) = app.viewer_shown.map(|s| s.0) else { return };
|
||||
if plain || app.viewer_pixels().is_none() {
|
||||
if !plain {
|
||||
ctx.request_repaint();
|
||||
|
||||
Reference in new issue
Block a user