Skip to content

perf(desktop): dispose the main window when it closes to the tray - #420

Merged
linroid merged 1 commit into
mainfrom
perf/desktop-dispose-hidden-window
Oct 7, 2026
Merged

linroid merged 1 commit into
mainfrom
perf/desktop-dispose-hidden-window

Conversation

@linroid

@linroid linroid commented Oct 7, 2026

Copy link
Copy Markdown
Owner

What

Closing the main window to the tray (menu bar on macOS) now disposes it instead of hiding it, as the Settings window already did. What its content saves with rememberSaveable (the page, the filter, the search, scroll positions) is kept in a SaveableStateHolder outside the window, so it reopens where it was left. Only transient overlays, such as an open command palette, reset.

Why

A hidden window kept its Skia/Metal device and its whole UI tree for as long as Ketch stayed in the tray, which is where a download manager spends most of its time.

Measurements

Apple Silicon, 1280 × 800 window, empty list, 60 s idle, physical footprint (what Activity Monitor shows):

Closed to the tray Before (hidden) After (disposed)
Footprint 405–460 MB 365–440 MB
GPU memory ~60 MB ~25 MB
Other window graphics ~18 MB ~1 MB
  • macOS keeps the window's frame buffers (78–110 MB of IOSurfaces) pooled per process either way, and macOS already freed part of the GPU memory when the window was only hidden, so the gain there is modest. Windows and Linux were not measured.
  • Four scripted close/reopen cycles showed no growth.

Also documented

docs/development/testing.md now records how to measure (footprint, not RSS), where the memory goes, and what was tried and left out:

  • Capping Skia's GPU cache: no effect at 32 MB, worse at 0.
  • Double buffering, Skia software rendering, Java2D on OpenGL: no help or worse.
  • Serial GC: about 20 MB less when idle, but 95th-percentile pauses of 32 ms instead of 3.5 ms while downloading at full speed, so G1 stays.

Testing

  • ./gradlew :app:desktop:test: 258 tests pass.
  • Packaged app on macOS: after closing to the tray the process owns no window; reopening (second launch or Dock) restores the same page, here Devices, at the same bounds.
  • Heap dumps after closing: the old UI tree is only held by the JDK's LWWindowPeer.lastCommonMouseEventPeer until another window gets a mouse event, with no native resources attached.

A hidden main window kept its Skia/Metal resources and its whole UI tree
for as long as Ketch stayed in the tray, which is where a download
manager spends most of its time. The window now exists only while it
shows, as the Settings window already did. What its content saves with
rememberSaveable (the page, the filter, the search, scroll positions) is
kept in a SaveableStateHolder outside the window and comes back when it
reopens.

On Apple Silicon (1280 x 800 window, empty list, idle), a window closed
to the tray now leaves 365-440 MB instead of 405-460 MB: GPU memory
falls to about 25 MB instead of 60 MB, and the rest of the window's
graphics memory to about 1 MB instead of 18 MB. Four close and reopen
cycles showed no growth.

The testing guide records how to measure this, and what was tried and
left out: capping Skia's GPU cache, double buffering, software
rendering, Java2D on OpenGL and the serial collector.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T02:52:45.086199Z 8104e24 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8104e24756

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

) {
val app = @Composable {
App(controller, activityEvents = activityEvents, fileLogger = launch.fileLogger)
windowStates.SaveableStateProvider(MAIN_WINDOW_STATE) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Save the list scroll state before disposing the window

When a user closes Ketch to the tray after scrolling the Downloads list, this provider does not preserve that position: DownloadsScreen creates DownloadsPage with remember, and DownloadsPage owns a plain LazyListState, so neither participates in this SaveableStateHolder. Removing the Window therefore destroys the list state and reopening starts at the top; make the list position saveable or hoist it outside the disposable window.

AGENTS.md reference: AGENTS.md:L512-L515

Useful? React with 👍 / 👎.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Test Results

 1 392 files  ±0   1 419 suites  ±0   9m 56s ⏱️ -8s
 9 144 tests ±0   8 915 ✅ ±0  229 💤 ±0  0 ❌ ±0 
10 483 runs  ±0  10 254 ✅ ±0  229 💤 ±0  0 ❌ ±0 

Results for commit 8104e24. ± Comparison against base commit e50759c.

This pull request removes 1 and adds 1 tests. Note that renamed tests count towards both.
com.linroid.ketch.app.desktop.CloseBehaviorTest ‑ backgroundLaunch_defersWindowUntilShownAndRetainsItWhenHiddenAgain
com.linroid.ketch.app.desktop.CloseBehaviorTest ‑ backgroundLaunch_createsTheWindowOnlyWhileItShows

@linroid
linroid merged commit 19e5026 into main Oct 7, 2026
13 checks passed
@linroid
linroid deleted the perf/desktop-dispose-hidden-window branch October 7, 2026 03:18
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