diff --git a/crates/render/src/retained_tests.rs b/crates/render/src/retained_tests.rs index 89b7b7a1e..518033aed 100644 --- a/crates/render/src/retained_tests.rs +++ b/crates/render/src/retained_tests.rs @@ -345,3 +345,31 @@ fn a_reused_or_patched_render_needs_no_steps() { let panned = View { origin: (12.0, 0.0), ..v }; assert!(matches!(r.begin(&s.doc, s.page, &panned, &Options::default(), 40), Some(crate::Begun::Done(_, _, Work::Partial { .. })))); } + +/// Background blurs stacked on each other (the upper reads the lower's blurred pixels) across the +/// band boundaries: the bands' overlap, the reach of the largest blur, is enough for the image to +/// match one drawn in one piece, as it is for the desktop's parallel bands. +#[test] +fn stacked_backdrop_blurs_draw_the_same_in_bands() { + fonts(); + let mut d = Document::new("stacked"); + let page = d.first_page().unwrap(); + // Stripes to blur, then three translucent blurred panels overlapping each other. + for i in 0..40 { + let r = d.create(page, NodeKind::Rectangle, "S", 0.0, f64::from(i) * 10.0, 400.0, 5.0).unwrap(); + solid(d.get_mut(r).unwrap(), if i % 2 == 0 { "E03030" } else { "3070E0" }); + } + for (x, y) in [(40.0, 60.0), (120.0, 120.0), (60.0, 200.0)] { + let b = d.create(page, NodeKind::Rectangle, "B", x, y, 220.0, 160.0).unwrap(); + d.get_mut(b).unwrap().fills = vec![Paint::solid(Color::new(1.0, 1.0, 1.0, 0.25))]; + d.get_mut(b).unwrap().effects.push(Effect { kind: EffectKind::BackgroundBlur(figcraft_doc::Blur::uniform(24.0)), visible: true }); + } + let doc = Arc::new(d); + let v = View { origin: (0.0, 0.0), scale: 1.0, width: 400, height: 400 }; + for band_h in [64, 100, 200] { + let mut r = Retained::default(); + let (shown, pix, n) = stepped(&mut r, &doc, page, &v, band_h); + assert!(n >= 3, "{n} bands of {band_h} rows"); + assert_same(&pix, &render_view(&doc, page, &shown, &Options::default()).unwrap(), &format!("bands of {band_h} rows")); + } +} diff --git a/crates/ui-egui/src/canvas/render_worker.rs b/crates/ui-egui/src/canvas/render_worker.rs index 0ffdd34fd..3d7659f56 100644 --- a/crates/ui-egui/src/canvas/render_worker.rs +++ b/crates/ui-egui/src/canvas/render_worker.rs @@ -42,9 +42,10 @@ pub const SETTLE_MS: u64 = 120; pub const STEP_MS: f64 = 24.0; /// Bands are at least this many rows ... const MIN_BAND_ROWS: u32 = 64; -/// ... and a view has at most this many: the longest frame of a render drawn in steps is about a -/// band's, an eighth of the render plus its overlap (more bands would repeat more of the overlap -/// and of the per-band work). +/// ... and a view has at most this many: a frame draws at least one band, so the longest frame of +/// a render drawn in steps is about a band's time, a part of the render rather than the whole of it +/// (0.7–1.4 s where the render took 3.7–4.0 s, measured for #1496; more bands would repeat more of +/// the overlap and of the per-band work). const MAX_BANDS: u32 = 8; #[derive(Clone)] diff --git a/docs/gaps.md b/docs/gaps.md index 537b63155..2c04ec5be 100644 --- a/docs/gaps.md +++ b/docs/gaps.md @@ -274,8 +274,9 @@ No alpha blockers: the alpha gate in [`roadmap.md`](roadmap.md#alpha-gate) passe page, a zoom after quick renders), a watchdog thread previews any such render still running after 12 ms (desktop only; the browser renders inline). - **Landed (2026-10-29):** the browser draws a zoom's full render a band at a time (`Retained::begin`, - `Steps`; at most eight bands, so the longest frame is about an eighth of the render where it was - the whole of it), so input is handled while it draws: a pan or an edit meanwhile is patched into + `Steps`; at least a band a frame and at most eight bands, so a frame draws a part of the render + where it drew the whole of it: 0.7–1.4 s where the render took 3.7–4.0 s in the measurement of + #1496), so input is handled while it draws: a pan or an edit meanwhile is patched into the render once drawn (or it is drawn again, the first shown till then), a zoom drops it. Bands overlap by what background blurs read, as the desktop's parallel bands do, and the bands together take longer than one render (each repeats its overlap and some per-band work).