diff --git a/Cargo.toml b/Cargo.toml
index fc93b0a..9da9005 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -146,8 +146,9 @@ opt-level = 0
debug = true
# `gpui-pre-wgpu` 0.3.8 with its headless renderer behind an `offscreen` feature
-# instead of `test-support` / `bench-support`, and a constructor that takes the
-# wgpu instance (iOS needs Metal). Applications that enable `offscreen` must
-# patch it the same way.
+# instead of `test-support` / `bench-support`, a constructor that takes the wgpu
+# instance (iOS needs Metal), and methods that release its render target and
+# clear its sprite atlas between captures. Applications that enable `offscreen`
+# must patch it the same way.
[patch.crates-io]
gpui-pre-wgpu = { path = "vendor/gpui-pre-wgpu" }
diff --git a/README.md b/README.md
index 0a9e83b..7fff63c 100644
--- a/README.md
+++ b/README.md
@@ -106,10 +106,17 @@ let image = window.render(size(px(390.), content_height))?; // image::RgbaImage
window.close(cx);
```
+Dropping an `OffscreenWindow` without calling `close` (for example when the task
+holding it is cancelled) still closes the window, on the next turn of the main
+thread's executor. The GPU device and pipelines are kept for later captures;
+once the last offscreen window closes, the render target sized to the last
+frame and the glyph and image tiles the offscreen windows uploaded are released.
+
It needs `gpui-pre-wgpu`'s headless renderer, which upstream only builds for
tests and benchmarks and which picks a Vulkan/GL device that iOS lacks. This
repository vendors `gpui-pre-wgpu` 0.3.8 with that renderer behind an
-`offscreen` feature and a constructor that takes the wgpu instance
+`offscreen` feature, a constructor that takes the wgpu instance, and methods
+that release its render target and clear its sprite atlas
(`vendor/gpui-pre-wgpu`). An application enabling `offscreen` must patch it the
same way:
diff --git a/src/offscreen.rs b/src/offscreen.rs
index 1f4f59e..61bc63c 100644
--- a/src/offscreen.rs
+++ b/src/offscreen.rs
@@ -9,17 +9,24 @@
//!
//! The renderer (one GPU device plus its pipelines) is created on first use and
//! kept for the rest of the process on the main thread, so later captures skip
-//! device creation and shader compilation.
-
-use std::{cell::RefCell, rc::Rc, sync::Arc};
+//! device creation and shader compilation. What a capture leaves on the GPU (the
+//! render target, sized to the last frame, and the glyph and image tiles of the
+//! atlas the offscreen windows share) is released once the last offscreen window
+//! closes.
+
+use std::{
+ cell::{Cell, RefCell},
+ rc::Rc,
+ sync::Arc,
+};
use anyhow::{Context as _, Result};
use gpui::{
- point, px, App, Bounds, Capslock, DevicePixels, DispatchEventResult, Entity, GpuSpecs,
- Modifiers, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler,
- PlatformWindow, Point, PromptButton, PromptLevel, Render, RequestFrameOptions, Scene, Size,
- Window, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea,
- WindowHandle, WindowOptions, WindowParams, WindowVisibility,
+ point, px, AnyWindowHandle, App, AppContext as _, AsyncApp, Bounds, Capslock, DevicePixels,
+ DispatchEventResult, Entity, GpuSpecs, Modifiers, Pixels, PlatformAtlas, PlatformDisplay,
+ PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptButton, PromptLevel, Render,
+ RequestFrameOptions, Scene, Size, Window, WindowAppearance, WindowBackgroundAppearance,
+ WindowBounds, WindowControlArea, WindowHandle, WindowOptions, WindowParams, WindowVisibility,
};
use gpui_wgpu::WgpuHeadlessRenderer;
pub use image::RgbaImage;
@@ -31,14 +38,21 @@ thread_local! {
static PENDING: RefCell