fix: keep windows sharp when they are scaled down - #170
Open
rcmz wants to merge 1 commit into
Open
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.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 literallywidth / 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
LinearFilterEffectcould 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
setCreateMipmaps, paired increateEffect/onRemoveEffect), so the offscreen texture gets a faithful copy of the window.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,LINEARwhen scaled down, andLINEAR_MIPMAP_LINEARbelow 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.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:
mainDesktop 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_targetrestores 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
firstChildthere. Comparing each build against the same scene with the extension disabled, using six xterms:main(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 .andjust buildboth 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-bytrailer.Happy to adjust the 0.25 mipmap threshold, split the commit, or drop the
LinearFilterEffectremoval into a separate change if you'd prefer.