diff --git a/crates/render/src/lib.rs b/crates/render/src/lib.rs index eaa31c5c5..fcb73fff5 100644 --- a/crates/render/src/lib.rs +++ b/crates/render/src/lib.rs @@ -734,6 +734,7 @@ impl Renderer { /// Start a frame: cache entries used from now on are marked with its stamp. fn begin_frame(&mut self) { self.stamp += 1; + self.inline_depth = 0; } /// End a frame: drop cache entries not used for a few frames (every render, thumbnails too, diff --git a/crates/render/src/pattern.rs b/crates/render/src/pattern.rs index 9cb5e874f..e6ea284ba 100644 --- a/crates/render/src/pattern.rs +++ b/crates/render/src/pattern.rs @@ -205,10 +205,18 @@ fn with_tile_renderer(f: impl FnOnce(&mut Renderer) -> T) -> Option { })) })?; let mut r = r; + // Restores the depth even if `f` panics and the panic is caught (the renderer is then dropped). + struct Restore; + impl Drop for Restore { + fn drop(&mut self) { + CACHE.with(|c| c.borrow_mut().depth -= 1); + } + } + let guard = Restore; let out = f(&mut r); + drop(guard); CACHE.with(|c| { let mut c = c.borrow_mut(); - c.depth -= 1; if c.renderer.is_none() { c.renderer = Some(r); } @@ -687,4 +695,13 @@ mod tests { assert_eq!(raster_scale(2.1, (10.0, 10.0)), raster_scale(3.9, (10.0, 10.0))); assert!(raster_scale(64.0, (1000.0, 10.0)) * 1000.0 <= MAX_TILE_PX); } + + #[test] + fn tile_renderer_depth_survives_caught_panics() { + for _ in 0..4 { + let r = std::panic::catch_unwind(|| with_tile_renderer(|_| panic!("tile boom"))); + assert!(r.is_err()); + } + assert!(with_tile_renderer(|_| ()).is_some(), "depth leaked after caught panics"); + } }