Skip to content

fix(desktop-gpui): release preview textures the GPU atlas keeps after dialogs close - #2801

Merged
richiemcilroy merged 1 commit into
mainfrom
fix/gpui-atlas-image-leaks
Oct 22, 2026
Merged

richiemcilroy merged 1 commit into
mainfrom
fix/gpui-atlas-image-leaks

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Oct 21, 2026 •

Copy link
Copy Markdown
Member

What

gpui's sprite atlas is per window and keyed by image id. Dropping the last Arc<RenderImage> does not free its tile or texture; only Window::drop_image does, and otherwise the texture stays until the window closes. These images were replaced or discarded without it, inside long-lived editor windows:

  • screenshot editor crop dialog (screenshot_crop.rs): the original PNG is decoded at full resolution for every open (5120x2880 is 59 MB) and dropped on Save/Cancel/Escape/backdrop without eviction
  • video editor crop (editor_crop.rs): same shape, the full-resolution display frame decoded per open, dropped on Save/Cancel without eviction; a late decode landing on a reopened crop also replaced a frame without eviction
  • gradient grain overlay (editor_sidebar.rs): a new noise texture per grain-scale step during a slider drag, each old one left in the atlas
  • video editor Image/Desktop background preview (editor_sidebar.rs ensure_preview): replaced on every new selection and cleared on tab switch without eviction
  • screenshot editor Image background preview (screenshot_editor.rs ensure_background_image): same

Fix

Mirrors the existing window.drop_image(previous) pattern (frame_arrived, MainWindow::set_recents, ThumbnailCache):

  • both crop dialogs evict their decoded image when they close (ScreenshotCropDialog::release_image, CropState::release_frame), and the video crop evicts a frame a late decode replaces
  • background previews evict the previous image on replace and on clear; their decode tasks now run with spawn_in(window) so the completion has a &mut Window
  • the grain cache (NoiseCache) queues replaced textures, and EditorWindow::render releases them on the next frame through SidebarState::release_retired_images, since the sidebar panel is built without a &mut Window

No behaviour change otherwise: the same images are shown, cached and re-decoded exactly as before; only atlas eviction is added. Images still on screen are never evicted (replace drops the previous one only after the new one is installed, and the grain queue drains one frame after the swap).

Other RenderImage producers checked and left alone: camera, editor preview, screenshot frames, recents/target thumbnails, settings avatars, export thumbnail (all already evict), clip thumbnails, wallpaper tiles, cursor and color preset tiles (bounded caches that are never replaced).

Tests

  • a_new_grain_step_retires_the_old_texture_for_atlas_eviction: every replaced grain texture is handed back for eviction exactly once, the current one never is

Measurements

Scratch probe on Blacksmith macOS 15 (run 39538128953): debug build of this branch, a generated 5120x2880 screenshot bundle, a scratch hook that opens the crop dialog, waits 4 s for decode and paint, cancels, and repeats 10 times. CAP_PROBE_NO_RELEASE=1 skips only the new drop_image call, so both rows come from the same binary. Numbers from COMET_GPU_STATS and footprint after the 10 cycles, two rounds each:

atlas textures atlas MB Metal device MB process footprint
without release, r1 13 623.8 1069.4 642 -> 1149 MB
without release, r2 13 623.8 1062.2 634 -> 1141 MB
with release, r1 3 61.2 506.9 642 -> 586 MB
with release, r2 3 61.2 506.9 644 -> 588 MB

Without the release every open leaves about 56 MB in the atlas (one 5K BGRA texture), so footprint climbs by about 50 MB per open. With it, the atlas is back to its pre-dialog size after every close (the 61.2 MB left is the editor's own 5K preview frame).

The video editor crop, grain and background preview paths are the same drop_image call and were not probed separately. The grain path is covered by the unit test.

RetriggerView in GreptileConfidence Score: 5/5

This PR appears safe to merge; no actionable issues were found.

Summary

This PR releases preview images from the window’s GPU atlas when they are no longer needed.

  • Screenshot crop dialogs release their full-size image when they close.
  • Video crop dialogs release decoded frames when they close or get replaced.
  • Background previews release old images when their source changes.
  • The grain slider releases textures from earlier scale steps.

No actionable issues were found. Build checks and runtime tests were not run.

Reviews (1) · Last reviewed commit: "fix(desktop-gpui): release preview textu..." · Reviewed by Greptile

@vercel

vercel Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cap-status Ready Ready Preview Oct 21, 2026 3:19am UTC

@blacksmith-sh

blacksmith-sh Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
cap-verify::crash/crash_safety View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@richiemcilroy

Copy link
Copy Markdown
Member Author

@greptileai review

@richiemcilroy
richiemcilroy marked this pull request as ready for review October 22, 2026 06:21
@richiemcilroy

Copy link
Copy Markdown
Member Author

@greptileai review

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Capy found no issues.

View 1 other finding in Capy.

Open in Capy

@richiemcilroy
richiemcilroy merged commit 6be6de4 into main Oct 22, 2026
63 of 67 checks passed

This branch was successfully deployed

2 active deployments
Preview – cap-web — c1ecf369 Deployed Oct 21, 2026 by vercel[bot]
Preview – cap-status — c1ecf369 Deployed Oct 21, 2026 by vercel[bot]
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