Skip to content

offscreen: Release the render target on close and close dropped windows - #37

Merged
madcodelife merged 2 commits into
mainfrom
offscreen-release
Oct 13, 2026
Merged

madcodelife merged 2 commits into
mainfrom
offscreen-release

Conversation

@madcodelife

@madcodelife madcodelife commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Summary

Follow-up to #33.

  • Release GPU memory after captures. The headless renderer is kept for the rest of the process, so after a capture it held the render target and the path intermediate / MSAA textures sized to the last frame (for a 1170×16000 image that is roughly 75 MB + 75 MB + 300 MB at 4× path MSAA), plus every glyph and image tile uploaded to the sprite atlas the offscreen windows share. GPUI only removes image tiles through App::drop_image on open windows, which never reaches that atlas once the window is gone, so image tiles accumulated for the life of the process.
    • The vendored gpui-pre-wgpu gains two offscreen-gated methods on WgpuHeadlessRenderer: release_render_target (render target + path intermediates) and clear_sprite_atlas (atlas tiles + the cached bind groups that keep their textures alive). The readback buffer was already a per-call local, so there is no staging buffer to release.
    • offscreen counts live offscreen platform windows and calls both when GPUI drops the last one (after its view tree), so no open window can hold a tile that gets cleared. The device and pipelines stay for the next capture.
  • Close dropped windows. Dropping an OffscreenWindow without close (for example when the task holding it is cancelled) used to leave the window and its view tree alive forever. It now keeps an AsyncApp and, on drop, spawns the close on the foreground executor, since Drop may run while the App is borrowed. close disarms it, so the window is never closed twice.
  • README and the [patch.crates-io] comment describe the new vendor methods and the release behaviour.

While an offscreen window stays open, image tiles still accumulate the same way they do for on-screen windows; the instance buffer, which grows to the largest frame's instance data, is kept with the pipelines.

Testing

  • cargo check --features offscreen for aarch64-apple-ios, aarch64-apple-ios-sim and aarch64-linux-android
  • cargo check --all-targets, cargo test --lib, cargo fmt --all -- --check
  • cargo clippy --features offscreen for iOS and Android: no new warnings
  • New vendor test headless_renderer_releases_frame_textures (Linux, like the other headless tests); also run locally on macOS against a Metal instance, where it passes

madcodelife and others added 2 commits October 13, 2026 15:38
The headless renderer lives for the rest of the process, so after a capture
it kept the render target and path textures sized to the last frame (a long
image is hundreds of megabytes of GPU memory) and every glyph and image tile
uploaded to the atlas the offscreen windows share: GPUI only removes image
tiles through `App::drop_image` on open windows, which never reaches it once
the window is gone.

Add `WgpuHeadlessRenderer::release_render_target` and `clear_sprite_atlas` to
the vendored `gpui-pre-wgpu`, and call both when GPUI drops the last offscreen
platform window. The device and pipelines are kept for the next capture.

`OffscreenWindow` now closes its window when dropped without `close` (for
example when the task holding it is cancelled). `Drop` has no `App`, so it
spawns the close on the foreground executor; `close` disarms it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@madcodelife
madcodelife merged commit 74881b5 into main Oct 13, 2026
5 checks passed
@madcodelife
madcodelife deleted the offscreen-release branch October 13, 2026 07:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant