Skip to content

BlurCache: don't rebuild the cache when dirtyRegion is empty - #94

Open
xxmathias wants to merge 1 commit into
xarblu:mainfrom
xxmathias:blurcache-skip-rebuild-on-empty-dirty-region
Open

BlurCache: don't rebuild the cache when dirtyRegion is empty#94
xxmathias wants to merge 1 commit into
xarblu:mainfrom
xxmathias:blurcache-skip-rebuild-on-empty-dirty-region

Conversation

@xxmathias

Copy link
Copy Markdown

Problem

When KWin paints a blurred window with damage that doesn't intersect backgroundRect (e.g. shadow-only damage on focus changes, or repaint slop from effects like wobbly windows), the dirtyRegion is empty: nothing behind the window was repainted this frame, so there are no fresh pixels to compare against and prepareCache() bails before setting up the conditional render.

Previously this caused an ungated re-blur: all blur passes ran and drawToCache() overwrote the cached texture — the only place the cache was rewritten outside the conditional-render gate. Besides wasting GPU time on every such paint, this rebuilt the cache from the reference blit alone (which can contain never-initialized regions) and re-applied the noise and rounded-corner passes to it.

This is suspected to contribute to blur showing a stale "cached old state" instead of tracking live content behind the window.

Fix

Track this case as useCachedOnly in the paint data and skip the scene blit, all blur passes and the cache writes, only drawing the existing cached texture.

If no cache entry exists yet and there is no fresh data, bail entirely — there is nothing valid to build an entry from, and the on-screen draw is clipped to the deviceRegion anyway.

Notes

  • colorMatrix/modulation declarations were hoisted above the new gate since drawCached() needs modulation outside it.
  • Follows the existing "indent intentional for KWin diff" style for the added braces to keep the diff against upstream kwin minimal.

🤖 Generated with Claude Code

When KWin paints the window with damage that doesn't intersect
backgroundRect (e.g. shadow-only damage on focus changes) the
dirtyRegion is empty: nothing behind the window was repainted, so
there are no fresh pixels to compare against and prepareCache()
bails before setting up the conditional render.

Previously this caused an *ungated* re-blur: all blur passes ran
and drawToCache() overwrote the cached texture - the only place
the cache was rewritten outside the conditional render. Besides
wasting GPU time on every such paint, this rebuilt the cache from
the reference blit alone, which can contain never-initialized
regions, and re-applied the noise and rounded corner passes to it.

Track this case as useCachedOnly in the paint data and skip the
scene blit, all blur passes and the cache writes, only drawing the
existing cached texture. If no cache entry exists yet there is
nothing valid to build one from - bail entirely; the on-screen
draw is clipped to the deviceRegion anyway.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@xxmathias
xxmathias force-pushed the blurcache-skip-rebuild-on-empty-dirty-region branch from 6c347e2 to f1551c1 Compare June 18, 2026 09:10
@xxmathias

Copy link
Copy Markdown
Author

Rebased this onto your BlurCache rework. The merge conflicts are gone now.

I kept the useCachedOnly guard since it still seems to matter even with the new isFlushing flow: when the timer elapses you flush even if accumulatedDirtyRegion is empty, so a flush frame can land with an empty dirtyRegion (shadow-only damage on focus change). Without the guard that case blits + rebuilds the cache from stale data. So it ends up complementing the rate limiting rather than overlapping with it.

No rush, just wanted to unblock it since it had conflicts. Happy to drop it if you'd rather fold it into the rework yourself.

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