Skip to content

fix: keep windows sharp when they are scaled down - #170

Open
rcmz wants to merge 1 commit into
flexagoon:mainfrom
rcmz:fix/overview-blur
Open

fix: keep windows sharp when they are scaled down#170
rcmz wants to merge 1 commit into
flexagoon:mainfrom
rcmz:fix/overview-blur

Conversation

@rcmz

@rcmz rcmz commented Aug 11, 2026

Copy link
Copy Markdown

Fixes #36 — windows with rounded corners are blurry and washed out in the overview.

That issue is labelled upstream issue, and the underlying bug (gnome-shell#7903) is real and still open. But it turns out the extension can side-step it, so this isn't a fix for the GNOME bug — it's a workaround that stops the extension from tripping over it.

final_compare

Same window preview in the overview, zoomed 4x — top: extension disabled, middle: current main, bottom: this PR

What actually goes wrong

GNOME paints a window from a half resolution copy of its texture once the window ends up smaller than half size on screen (MetaTextureMipmap — level 0 of that copy is literally width / 2 × height / 2).

The corner rounding shader is a Clutter.OffscreenEffect, so a window that has it is first rendered into an offscreen texture at its full size, and that texture is painted in place of the window. GNOME still makes the half resolution decision from the size the final result lands at, even while it is filling a full size offscreen texture. So the offscreen texture ends up holding a blurry, upscaled copy of the window, and the detail is gone before the texture is ever painted.

That last part is why LinearFilterEffect could only ever help a little: it changed how the result was filtered, but the detail had already been thrown away one step earlier. I measured every filter option on the offscreen texture (NEAREST, LINEAR, both mipmap modes) and all of them stayed 3-8x below the sharpness of a window without the effect.

What this changes

  • Turns mipmaps off on the window's texture while the effect is attached (setCreateMipmaps, paired in createEffect / onRemoveEffect), so the offscreen texture gets a faithful copy of the window.
  • Adds effect/texture_filters.ts, which picks the filters for the offscreen texture per paint, based on how much it is scaled down: GNOME's own choice at 1:1, LINEAR when scaled down, and LINEAR_MIPMAP_LINEAR below 0.25 — window previews in the workspace switcher are painted at about 4% and turn into speckles otherwise. This is the part GNOME can no longer do for us.
  • Removes LinearFilterEffect. It added an extra full size offscreen pass over every window preview, only ever applied to previews of windows that had a shadow, and could not undo the detail that was already lost.

Testing

Tested manually on GNOME 50 (Wayland) at 4K, and measured in a headless GNOME Shell 48.7 session (gnome-shell --headless --virtual-monitor 1920x1080) with six identical terminal windows, comparing against the exact same scene with the extension disabled.

Sharpness is the variance of the Laplacian over the window preview area; diff is the mean absolute pixel difference from the extension-disabled reference, on a 0-255 scale:

previews diff thumbnails diff
extension disabled (ref) 9162 0.000 495 0.000
current main 1155 7.636 1108 1.179
this PR 9191 0.556 428 0.423

Desktop rendering at 1:1 is unchanged: 97.2% of pixels in the window area are identical with and without the extension, the same as before this change. vfunc_paint_target restores the filter GNOME picked whenever nothing is scaled down, so nothing changes outside the overview.

X11 windows were measured separately, since the effect lives on firstChild there. Comparing each build against the same scene with the extension disabled, using six xterms:

on main with this PR
XWayland 20% 99.7%
GNOME X11 session 22% 99.96%

(percentages are preview sharpness as a fraction of the extension-disabled reference in the same session)

Also checked: rounded corners and overview shadows still render, and biome ci . and just build both pass.

Disclosure

Per the PR template: this change is mostly AI-authored (Claude Opus 5, via Claude Code), including the investigation of the root cause. I reviewed it and tested the result on my own machine before opening this PR, and the numbers above come from actual runs, not from the model's description of them. The commit carries a Co-authored-by trailer.

Happy to adjust the 0.25 mipmap threshold, split the commit, or drop the LinearFilterEffect removal into a separate change if you'd prefer.

Windows with rounded corners looked blurry and washed out whenever they
were painted scaled down, most visibly as previews in the overview.

The corner rounding shader is a Clutter.OffscreenEffect, so a window that
has it is first rendered into an offscreen texture at its full size, and
that texture is painted in place of the window. GNOME picks how to paint a
window texture based on how large it ends up on screen: below half size it
paints from a half resolution copy of the texture instead. For a window
with an effect it still makes that decision from the size of the final
result, even while it is filling a full size offscreen texture, so the
offscreen texture ended up holding a blurry, upscaled copy of the window,
and no amount of filtering afterwards could bring the detail back.

Turn those mipmaps off for windows that have the effect, and pick the
filters for the offscreen texture based on how much it is actually scaled
down while being painted, which is what GNOME does for window textures.

This also replaces LinearFilterEffect, which tried to approximate the same
thing with an extra offscreen pass over each window preview: it only ever
applied to previews of windows that had a shadow, and it could not undo
the detail that was already lost.

Fixes flexagoon#36
See https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/7903

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

Blurry apps in overview

1 participant