Skip to content

perf(wallpapers): cap the bundled backgrounds at 3840px - #542

Merged
EtienneLescot merged 1 commit into
mainfrom
fix/wallpaper-resolution-cap
Aug 30, 2026
Merged

perf(wallpapers): cap the bundled backgrounds at 3840px#542
EtienneLescot merged 1 commit into
mainfrom
fix/wallpaper-resolution-cap

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

The bundled wallpapers shipped at up to 7680×7680, for an app whose largest output is 4K. That is resolution nobody can see, paid for three times: on disk, in decode time, and in VRAM for as long as the texture stays resident.

17 of 18 resized to 3840 on the long edge (Lanczos, quality 92).

before after
on disk 23.7 MB 13.9 MB
all 18 resident 1774 MB 772 MB
a typical two-wallpaper session 450 MB 112 MB
decoding all 18 3.31 s 1.43 s

Why 3840, and why this is not a quality trade

3840 is exactly 1:1 for a 4K output — below it, a 4K export would sample a background softer than its own frame. At 1080p the texture is still oversampled 2:1. So nothing is lost at any output size we support; this removes detail no pipeline could reach.

Encoding loss measured per file: 41.3 to 52.1 dB PSNR against the raw Lanczos downscale. Above 40 dB is visually transparent on photographic content, and the worst case is the most detailed image.

ICC profiles are preserved byte for byte — five files carry one, and losing it would shift colours, which is a visible regression where the resolution is not. EXIF rides along untouched. Thumbnails are already 240×240 and unchanged.

Why the assets and not the code

A runtime cap was measured first, and rejected:

  • CPU resize: image::imageops::resize costs 217–730 ms per first use in release — two to four times the decode it follows. Both strategies tried (one resize, successive halvings) cost the same. Adding up to 730 ms to picking a wallpaper to save memory is a bad trade.
  • GPU downscale: cheap at runtime, but load_image_srv runs mid-frame, so it would have to save and restore the render target and viewport across three back-ends. Real hazard, marginal gain.

Capping the assets costs nothing at runtime and shrinks the installer too.

It does not cover wallpapers the user imports. Those stay bounded by the LRU budget from #537 — the safety net this sits on top of rather than replaces.

One consequence worth naming

img_cache_stays_under_budget now holds 11–13 entries resident instead of 3, which is the real win: far less eviction churn. Its anti-vacuous guard (unevicted total must exceed the budget) now reads 772 MB against 512. Still true, but closer to the floor — shrink these assets much further and that test will fail, which is exactly the signal it exists to give.

Verification

2208 JS tests, 151 Rust, docs check clean.

🤖 Generated with Claude Code

They shipped at up to 7680x7680 for an app whose largest output is 4K. That is
resolution nobody can see, paid for three times: on disk, in decode time, and
in VRAM for as long as the texture stays resident.

17 of 18 resized to 3840 on the long edge, Lanczos, quality 92. Measured:

| | before | after |
|---|---|---|
| on disk | 23.7 MB | 13.9 MB |
| all 18 resident | 1774 MB | 772 MB |
| a typical two-wallpaper session | 450 MB | 112 MB |
| decoding all 18 | 3.31 s | 1.43 s |

3840 rather than something smaller because it is exactly 1:1 for a 4K output —
below it, a 4K export would sample a background softer than its own frame. At
1080p the texture is still oversampled 2:1. So this is not a quality trade at
any output size we support; it is removing detail that no pipeline could reach.

The encoding loss is 41.3 to 52.1 dB PSNR against the raw Lanczos downscale,
measured per file. Above 40 dB is visually transparent on photographic
content, and the worst case is the most detailed image.

ICC profiles are preserved byte for byte — five files carry one, and losing it
would shift colours, which is a visible regression where the resolution is
not. EXIF rides along untouched. Thumbnails are already 240x240 and unchanged.

WHY THE ASSETS AND NOT THE CODE. A runtime cap was measured first and rejected:
`image::imageops::resize` costs 217 to 730 ms per first use in release, two to
four times the decode it follows, and the two strategies tried (one resize,
successive halvings) cost the same. A GPU downscale would be cheap but
`load_image_srv` runs mid-frame, so it would have to save and restore the
render target and viewport across three back-ends — real hazard, marginal gain.
Capping the assets costs nothing at runtime and shrinks the installer too.

It does not cover wallpapers the user imports. Those stay bounded by the LRU
budget from #537, which is the safety net this optimisation sits on top of
rather than replaces.

One consequence worth naming: `img_cache_stays_under_budget` now holds 11-13
entries resident instead of 3, which is the real win — far less eviction churn.
Its anti-vacuous guard (unevicted total must exceed the budget) now reads
772 MB against 512. Still true, but closer to the floor: shrink these assets
much further and that test will fail, which is exactly the signal it exists to
give.
@coderabbitai

coderabbitai Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (17)
  • public/wallpapers/wallpaper1.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper10.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper11.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper12.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper13.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper14.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper15.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper16.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper17.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper18.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper3.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper4.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper5.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper6.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper7.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper8.jpg is excluded by !**/*.jpg
  • public/wallpapers/wallpaper9.jpg is excluded by !**/*.jpg

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ef2e061d-042a-4239-b26e-c77fe46abfc5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@EtienneLescot
EtienneLescot merged commit f22a3a9 into main Aug 30, 2026
17 checks passed
@EtienneLescot
EtienneLescot deleted the fix/wallpaper-resolution-cap branch August 30, 2026 17:34
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