From d344bc604819cc97912c0f7deccaa3753f7d8c55 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Thu, 15 Oct 2026 06:36:27 -0700 Subject: [PATCH] fix(draw): pictures shrink with the optimised filter in debug builds RasterImage::decode called imageops::resize, a generic function, so the Triangle filter was compiled with draw at opt-level 0 instead of with image at the opt-level 3 debug builds give dependencies. A 1024 px picture took about 240 ms to shrink rather than 11 ms. The budget test shrinks twenty of them one after another on the single picture thread, about five seconds on an idle machine and past its twenty-second limit under load. Going through DynamicImage::resize_exact runs the same filter as image compiled it. Release builds were unaffected. Under the same load, a_missing_section_file_reports_its_error_until_it_returns failed for a separate reason. sync_status read the last sync time before it took the observed error, so a step that finished in between showed its cleared error next to the previous step's time. The time is now read under the observed lock, after the error. Fixes #149 Assisted-by: claude-opus-5.5 --- crates/draw/src/render.rs | 11 +++++------ crates/notebook/src/background.rs | 2 +- crates/notebook/src/session.rs | 8 +++++--- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/crates/draw/src/render.rs b/crates/draw/src/render.rs index beea59ae80abf3ccab35fd4a44dabbe81ed7155e..bad59d61bcccaa5889404d910b35fec067f1143d 100644 --- a/crates/draw/src/render.rs +++ b/crates/draw/src/render.rs @@ -137,12 +137,11 @@ impl RasterImage { let size = [pixels.width(), pixels.height()]; let shown = [0, 1].map(|axis| size[axis].min(within[axis]).max(1)); if shown != size { - pixels = image::imageops::resize( - &pixels, - shown[0], - shown[1], - image::imageops::FilterType::Triangle, - ); + // Through `DynamicImage` the filter is compiled in `image`, optimised in debug builds + // too; `imageops::resize` called here would be compiled unoptimised with this crate. + pixels = image::DynamicImage::from(pixels) + .resize_exact(shown[0], shown[1], image::imageops::FilterType::Triangle) + .into_rgba8(); } Self::premultiplied(shown, pixels.into_raw()) } diff --git a/crates/notebook/src/background.rs b/crates/notebook/src/background.rs index ea43b8477df8bcb5e6a42dc5c30a5fa6afc3b96e..ebeece4de03bf00a51446767d056f4e532ea19e9 100644 --- a/crates/notebook/src/background.rs +++ b/crates/notebook/src/background.rs @@ -495,7 +495,7 @@ impl Background { let replica = held.replica.upgrade()?; let observed = held.observed.upgrade()?; let synced = - Some(worker.synced.load(Ordering::Acquire)).filter(|time| *time != 0); + || Some(worker.synced.load(Ordering::Acquire)).filter(|time| *time != 0); Some(sync_status(&replica, synced, &observed)) }); let status = match live { diff --git a/crates/notebook/src/session.rs b/crates/notebook/src/session.rs index 603d56e2e1695b8107a46c211b86b92d8568f668..93cc3a8f2e5bd4d95395b7d59a86cbae5ffe36c6 100644 --- a/crates/notebook/src/session.rs +++ b/crates/notebook/src/session.rs @@ -2198,7 +2198,7 @@ pub(crate) type Observed = Option>; pub(crate) fn sync_status( replica: &Replica, - synced: Option, + synced: impl FnOnce() -> Option, observed: &Mutex, ) -> Result { let queued: i64 = crate::lock(&replica.section.connection)?.query_row( @@ -2210,7 +2210,9 @@ pub(crate) fn sync_status( .lock() .map_err(|_| io::Error::other("Synchronization observer panicked"))?; Ok(SyncStatus { - synced, + // Read after `observed`: the worker stores the time before it clears the error, so a + // cleared error never shows beside the time of the step before it. + synced: synced(), error: observed .as_ref() .and_then(Option::as_ref) @@ -2732,7 +2734,7 @@ impl Section { pub fn sync_status(&self) -> Result { sync_status( &self.replica, - self.worker.as_ref().and_then(SyncWorker::synced), + || self.worker.as_ref().and_then(SyncWorker::synced), &self.observed, ) } -- 2.54.0