diff --git a/src/primitives/retired.rs b/src/primitives/retired.rs index 2e5badb..a5346b2 100644 --- a/src/primitives/retired.rs +++ b/src/primitives/retired.rs @@ -14,9 +14,12 @@ impl Retired { self.0.push(image); } - /// Frees every retired image; call while painting. + /// Frees images no one else holds; call while painting. pub fn release(&mut self, window: &mut Window) { - for image in self.0.drain(..) { + // A shared image may still be drawn; it waits. + let (free, held): (Vec<_>, Vec<_>) = self.0.drain(..).partition(|image| Arc::strong_count(image) == 1); + self.0 = held; + for image in free { if let Err(error) = window.drop_image(image) { log::error!("images: a retired image stayed: {error}"); } diff --git a/src/primitives/tests.rs b/src/primitives/tests.rs index cfa70f9..3bf58aa 100644 --- a/src/primitives/tests.rs +++ b/src/primitives/tests.rs @@ -261,4 +261,17 @@ fn a_retired_image_is_freed_at_the_next_paint(cx: &mut TestAppContext) { }); cx.run_until_parked(); assert!(view.read_with(cx, |painted, _| painted.retired.is_empty()), "the old image was freed"); + // One still held elsewhere waits until it is let go. + let pixels = image::RgbaImage::from_pixel(2, 2, image::Rgba([4, 5, 6, 255])); + let shared = std::sync::Arc::new(gpui::RenderImage::new([image::Frame::new(pixels)])); + view.update(cx, |painted, cx| { + painted.retired.push(shared.clone()); + cx.notify(); + }); + cx.run_until_parked(); + assert!(!view.read_with(cx, |painted, _| painted.retired.is_empty()), "a held image waits"); + drop(shared); + view.update(cx, |_, cx| cx.notify()); + cx.run_until_parked(); + assert!(view.read_with(cx, |painted, _| painted.retired.is_empty()), "then it is freed"); }