Retired: free images a view stopped drawing
GPUI keeps every painted RenderImage in the window's atlas until drop_image; views that repaint tiles grew without bound.
This commit is contained in:
3 files changed
+73
-1
No files matched your search
@@ -9,6 +9,7 @@ mod image;
|
|||||||
mod layer;
|
mod layer;
|
||||||
mod measure;
|
mod measure;
|
||||||
mod pressable;
|
mod pressable;
|
||||||
|
mod retired;
|
||||||
mod severity;
|
mod severity;
|
||||||
mod tooltip;
|
mod tooltip;
|
||||||
|
|
||||||
@@ -31,5 +32,6 @@ pub(crate) use image::{checked_ratio, framed};
|
|||||||
pub use layer::{Raised, raise};
|
pub use layer::{Raised, raise};
|
||||||
pub use measure::{IntersectionObserver, Measure};
|
pub use measure::{IntersectionObserver, Measure};
|
||||||
pub use pressable::Pressable;
|
pub use pressable::Pressable;
|
||||||
|
pub use retired::Retired;
|
||||||
pub use severity::Severity;
|
pub use severity::Severity;
|
||||||
pub use tooltip::{HelpTooltip, Tooltip, TooltipTrigger};
|
pub use tooltip::{HelpTooltip, Tooltip, TooltipTrigger};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
//! Images a view stopped drawing, freed from the window's atlas.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use gpui::{RenderImage, Window};
|
||||||
|
|
||||||
|
/// Images to free; GPUI keeps each painted image until told.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Retired(Vec<Arc<RenderImage>>);
|
||||||
|
|
||||||
|
impl Retired {
|
||||||
|
/// An image no longer drawn; freed at the next paint.
|
||||||
|
pub fn push(&mut self, image: Arc<RenderImage>) {
|
||||||
|
self.0.push(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Frees every retired image; call while painting.
|
||||||
|
pub fn release(&mut self, window: &mut Window) {
|
||||||
|
for image in self.0.drain(..) {
|
||||||
|
if let Err(error) = window.drop_image(image) {
|
||||||
|
log::error!("images: a retired image stayed: {error}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether nothing waits to be freed.
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.0.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
-1
@@ -217,8 +217,48 @@ fn f6_leaves_a_field_that_keeps_tab(cx: &mut TestAppContext) {
|
|||||||
let after = view.read_with(cx, |kept, _| kept.after.clone());
|
let after = view.read_with(cx, |kept, _| kept.after.clone());
|
||||||
cx.simulate_keystrokes("f6");
|
cx.simulate_keystrokes("f6");
|
||||||
assert_eq!(view.read_with(cx, |kept, _| kept.indents), 1, "Tab stayed in the field");
|
assert_eq!(view.read_with(cx, |kept, _| kept.indents), 1, "Tab stayed in the field");
|
||||||
cx.update(|window, cx| assert!(after.is_focused(window), "F6 moved on"));
|
cx.update(|window, _| assert!(after.is_focused(window), "F6 moved on"));
|
||||||
cx.simulate_keystrokes("shift-f6");
|
cx.simulate_keystrokes("shift-f6");
|
||||||
let field = view.read_with(cx, |kept, _| kept.field.clone());
|
let field = view.read_with(cx, |kept, _| kept.field.clone());
|
||||||
cx.update(|window, _| assert!(field.is_focused(window), "Shift-F6 came back"));
|
cx.update(|window, _| assert!(field.is_focused(window), "Shift-F6 came back"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A view that paints one image until told to retire it.
|
||||||
|
struct Painted {
|
||||||
|
image: Option<std::sync::Arc<gpui::RenderImage>>,
|
||||||
|
retired: super::Retired,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for Painted {
|
||||||
|
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let view = cx.entity();
|
||||||
|
gpui::canvas(
|
||||||
|
|_, _, _| {},
|
||||||
|
move |bounds, _, window, cx| {
|
||||||
|
view.update(cx, |painted, _| {
|
||||||
|
painted.retired.release(window);
|
||||||
|
if let Some(image) = painted.image.clone() {
|
||||||
|
window.paint_image(bounds, bounds, Default::default(), image, 0, false).expect("paints");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.size(px(10.0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
fn a_retired_image_is_freed_at_the_next_paint(cx: &mut TestAppContext) {
|
||||||
|
cx.update(Theme::init);
|
||||||
|
let pixels = image::RgbaImage::from_pixel(2, 2, image::Rgba([1, 2, 3, 255]));
|
||||||
|
let image = std::sync::Arc::new(gpui::RenderImage::new([image::Frame::new(pixels)]));
|
||||||
|
let (view, cx) = cx.add_window_view(|_, _| Painted { image: Some(image), retired: super::Retired::default() });
|
||||||
|
cx.run_until_parked();
|
||||||
|
view.update(cx, |painted, cx| {
|
||||||
|
let old = painted.image.take().expect("painted");
|
||||||
|
painted.retired.push(old);
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
cx.run_until_parked();
|
||||||
|
assert!(view.read_with(cx, |painted, _| painted.retired.is_empty()), "the old image was freed");
|
||||||
|
}
|
||||||
Reference in new issue
Block a user