Skip to content

The Blur Pull Request#3483

Open
YaLTeR wants to merge 15 commits intomainfrom
wip/branch
Open

The Blur Pull Request#3483
YaLTeR wants to merge 15 commits intomainfrom
wip/branch

Conversation

@YaLTeR
Copy link
Member

@YaLTeR YaLTeR commented Feb 21, 2026

Well, looks like I'm ready to promote this branch to a pull request. All planned features are implemented, all bugs that I knew about and wanted to fix should be fixed.

Please test and report any problems and suggestions. See the docs at https://github.com/niri-wm/niri/blob/wip/branch/docs/wiki/Window-Effects.md.

image

There's no "alpha threshold" setting for blur, instead clients are encouraged to implement the ext-background-effect protocol which lets them shape their background blur. It's already implemented, or in progress, in:

Note

Currently, this PR doesn't implement ext-background-effect for popups and subsurfaces. This is mainly because I don't have any clients that do this to test with. If some client can do it I'll look into it.

This PR also implements the KDE blur protocol which makes a bunch of other clients work (Alacritty, kitty, etc.) but I'll remove it before merging because even KDE has dropped it in favor of ext-background-effect.

Background blur turned out to be a massive undertaking. Not because of blur itself, but because window background effects in general required a lot of thinking and additions to the code, especially to make them as efficient as possible. Xray and non-xray background effects, both of which this PR implements, are also pretty much two entirely separate and very different beasts, both of which I had to get working with all other niri features (like block-out-from).

What's left is some code cleanups, and maybe splitting the commits a bit further.

Non-xray might land a bit later than xray because it depends on a WIP Smithay PR.

Implements #54, supersedes #1634. Thanks @visualglitch91 for rebasing and maintaining the previous blur PR all this time!

This was referenced Feb 21, 2026
use smithay::utils::{Logical, Rectangle};
use smithay::wayland::compositor::{RectangleKind, RegionAttributes};

pub fn region_to_non_overlapping_rects(
Copy link

Choose a reason for hiding this comment

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

Heya, I also implemented the kde blur and ext-background-effect protcols in my fork.

While doing that, I noticed that a lot of clients just define a single rectangle as their region, which encompasses the entire window / layer. This led to me developing this helper utility:

https://github.com/Naxdy/niri/blob/7b713f75d6340eea4a8bf9bf97e4bad8fdc0f21f/src/utils/region.rs

The helper exists mainly to avoid having to allocate an extra Vec for clients that only define one rectangle. There are other optimizations as well that help for checking whether a region changed (and then not re-render blur if that's the case), but those may not be applicable in your case.

Feel free to take a look and see if it's useful for you.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hey, guess this is more of a Smithay question because it already gives us a pre-allocated Vec: https://smithay.github.io/smithay/smithay/wayland/background_effect/struct.BackgroundEffectSurfaceCachedState.html

As for KDE-blur, I probably won't keep the impl in niri so it doesn't matter.

Other than that, in this PR I already made the computations as lazy as possible, so the non-overlapping region is only computed when the window-submitted wl_region actually changes, and only when it's first needed. And changes to the region don't cause blur re-renders.

@nishiiko
Copy link

setting xray false for all windows makes the blur only apply when the opening animation fully finishes
it can be sort of worked around by only setting xray false for floating windows only instead, but it ends up feeling a little clunky as everything behind a floating window disappears immediately during the anmiation of floating -> tiled mode

2026-02-21.14-22-33.mp4
2026-02-21.14-25-18.mp4

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 21, 2026

Yes, that's one of the known limitations of non-xray:

They disappear during window open/close animations and while dragging a tiled window. Fixing this requries subframe support in the Smithay rendering code.

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 21, 2026

everything behind a floating window disappears immediately during the anmiation of floating -> tiled mode

I'll have to think if there's a non-cursed way of delaying the xray change until the end of the animation, but probably there isn't any non-cursed way

@ryzendew
Copy link

image after doing some testing not finding any issues yet just noise looks really bad sadly. nice work!

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 21, 2026

just noise looks really bad sadly

hm?

@Suya1671
Copy link
Contributor

just noise looks really bad sadly

hm?

Compared to hyprlands or naxdy's fork, the noise looks rather... uniform? at higher values the pattern is very noticable/distracting.

image (0.2)

maybe either increase the transparency of the noise or it's distribution? idk

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 21, 2026

Yeah but the default value is 0.02, ofc if you make it higher it will be more visible. Is it noticeable at lower values or do you need higher values for some reason?

@BBaoVanC
Copy link

Might be good to add foot to the list of programs with ext-background-effect-v1 support in progress: https://codeberg.org/dnkl/foot/pulls/2198

@s3bba
Copy link

s3bba commented Feb 21, 2026

Is it noticeable at lower values or do you need higher values for some reason?

For me, the 0.02 setting at 4k and 2.4K screen resolutions is not noticeable, even when looking closely for it. I had to take a screenshot and zoom in really far to be able to see any noise patterns.

@seadx6-neo
Copy link

Dude! I hope this gets pulled and gets released soon! Using Noctalia, it would make everything look amazing

@yuxqiu
Copy link
Contributor

yuxqiu commented Feb 22, 2026

I was experimenting with blurring Firefox using this customChrome.css together with the following window rule in niri (Firefox 147 with vertical sidebar enabled):

window-rule {
    match app-id="firefox"
    background-effect {
        blur true
    }
}

Everything works fine at first. However, whenever I expand the vertical sidebar (either by making it to show full tab titles or by clicking the settings/bookmarks icons on the sidebar), strange glitchy black pixels appear in the background of the expanded sidebar. Sometimes black pixels also show up when I move the mouse over that area.

Interestingly, switching to another window and then back to Firefox resolves the glitch: the blur is applied correctly again with no visible artifacts. However, this fix is only temporary: as soon as I hide the sidebar again, switch to another window and back, and then re-expand the sidebar, the glitchy black pixels reappear.

One additional clue that might help narrow it down: whenever I take a screenshot or record a video of the glitched area, the captured image/video correctly shows the blur with no black pixel artifacts at all.

I'm not sure whether this is a firefox quirk or something on niri's side. I figured it could be worth bringing it up here first in case these are indeed artifacts that shouldn't be happening.

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 22, 2026

Everything works fine at first. However, whenever I expand the vertical sidebar (either by making it to show full tab titles or by clicking the settings/bookmarks icons on the sidebar), strange glitchy black pixels appear in the background of the expanded sidebar. Sometimes black pixels also show up when I move the mouse over that area.

This is usually a clientside bug when the client tells niri that some part of a Wayland surface is fully opaque when it isn't. GTK 3 tends to do this with rounded surfaces and the usual fix is to make them have background opacity 0.99 instead of 1. Firefox does custom Wayland handling, so not sure how you can fix it there.

You can check that this is the issue by binding https://niri-wm.github.io/niri/Configuration%3A-Debug-Options.html#debug-toggle-opaque-regions then seeing if the surface in question is blue (which is opaque) instead of red (semitransparent).

@yuxqiu
Copy link
Contributor

yuxqiu commented Feb 22, 2026

You can check that this is the issue by binding niri-wm.github.io/niri/Configuration%3A-Debug-Options.html#debug-toggle-opaque-regions then seeing if the surface in question is blue (which is opaque) instead of red (semitransparent).

Thanks! Yeah, indeed I noticed that the expanded region isn't fully red. It's a bit bluish compared to the titlebar and the unchanged sidebar region. Also, can confirm that setting background opacity to 0.99 fixes the problem.

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 22, 2026

Non-xray blur "fades out" near the very edges of a monitor. This happens because when we copy the framebuffer contents with glBlitFramebuffer(), it skips pixels outside the monitor bounds. I'm not actually sure why, since the documentation indicates it should clamp the pixel values to edge in this case. If we get this working, blur "fading out" would be fixed.

This should now be fixed, hopefully without any regressions.

@wrldspawn
Copy link

Being able to additionally change the contrast instead of only saturation would be nice to have

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 22, 2026

What's the usecase?

@wrldspawn
Copy link

What's the usecase?

I personally use 0.8 contrast and 0.2 saturation to make the blur desaturated in a way that I like with the wallpaper I use.

image

swayfx and kwin-effects-better-blur-dx (used in the screenshot) also have contrast and brightness options for blur.

@nonetrix
Copy link

There's no "alpha threshold" setting for blur, instead clients are encouraged to implement the ext-background-effect protocol which lets them shape their background blur. It's already implemented, or in progress, in:

Why not both?

@nonetrix
Copy link

nonetrix commented Feb 22, 2026

Yeah but the default value is 0.02, ofc if you make it higher it will be more visible. Is it noticeable at lower values or do you need higher values for some reason?

I think the issue is more that there is a obvious repeating pattern it isn't random never noticed this on Hyprland when setting the noise higher

Regardless, tested it and it's working well so far besides Quickshell/DMS not supporting ext-background-effect

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 22, 2026

Why not both?

  • it would be somewhat involved to support in niri
  • less incentive for clients to implement ext-background-effect when it's ultimately the better option (works across multiple compositors, doesn't require user configuration, less brittle)

I think the issue is more that there is a obvious repeating pattern it isn't random never noticed this on Hyprland when setting the noise higher

Yeah, this is just what this noise looks like, that I picked for niri (Interleaved Gradient Noise). Upsides are that it's extremely cheap and is supposed to work well specifically for dithering (which is kinda what it's used for in niri). So unless it's problematic I'd like to leave it as is

@nonetrix
Copy link

Yeah, this is just what this noise looks like, that I picked for niri (Interleaved Gradient Noise). Upsides are that it's extremely cheap and is supposed to work well specifically for dithering (which is kinda what it's used for in niri). So unless it's problematic I'd like to leave it as is

Having noise for the visuals can be kinda nice as well so both would be ideal, obviously not as extreme as that screenshot but I have used noise at a higher level that this would be noticeable really worked with one wallpaper I had at some point

@Fireye04
Copy link
Contributor

Fireye04 commented Feb 22, 2026

everything behind a floating window disappears immediately during the anmiation of floating -> tiled mode

I'll have to think if there's a non-cursed way of delaying the xray change until the end of the animation, but probably there isn't any non-cursed way

Would a gradual fade animation (spawn, no blur -> full blur) make sense here? Would delay the actual blur effect a bit but also make the pop-in less noticeable.

@YaLTeR
Copy link
Member Author

YaLTeR commented Feb 23, 2026

Would a gradual fade animation (spawn, no blur -> full blur) make sense here? Would delay the actual blur effect a bit but also make the pop-in less noticeable.

I'm afraid this doesn't help the technical challenge here, in fact even makes it a bit more cursed

@YaLTeR
Copy link
Member Author

YaLTeR commented Mar 7, 2026

  • Multiple clones of a non-xray background effect will interfere with each other and cause visual glitches. You can see this if you enable non-xray effects on a bottom or background layer surface, then open the Overview. Bottom and background layer surfaces are cloned on all workspaces that you can see in the Overview, causing interference. Fixing this requires support for framebuffer effect clones in the Smithay rendering code.

This non-xray limitation is now fixed, but this (admittedly quite rare) case is currently not optimized (waiting for more fixes to the Smithay PR).

@evan-goode
Copy link

In what color space is the blur being performed? From a glance at the shaders it looks like it's plain sRGB. Wouldn't we want something like srgb_to_linear as done in border.frag so that the color blending is done in a linear color space, and then convert back to sRGB?

minutephysics has a classic video on why blending colors in gamma space (e.g. sRGB) is wrong: https://www.youtube.com/watch?v=LKnqECcg6Gw.

@LasseRosenow
Copy link

In what color space is the blur being performed? From a glance at the shaders it looks like it's plain sRGB. Wouldn't we want something like srgb_to_linear as done in border.frag so that the color blending is done in a linear color space, and then convert back to sRGB?

But srgb-linear is not really nice when looking at the human eye. It maybe avoids the muddy color in between, but also creates very strange gradients for example from white to black:

image

In this case the color in the middle is already very very bright for the human eye. Even though it is "linear" from a photon intensity perspective, it is not from the point of the human eye reading it.

I would propose to maybe use a different color space such as oklab or similar? They solve the muddy problem, but also stay more close to how the human eye reads gradients.

A good example of gradients in different color spaces can be found on this website:
https://codepen.io/argyleink/pen/OJObWEW

@Koranir
Copy link

Koranir commented Mar 10, 2026

But srgb-linear is not really nice when looking at the human eye. It maybe avoids the muddy color in between, but also creates very strange gradients for example from white to black:
image

In this case the color in the middle is already very very bright for the human eye. Even though it is "linear" from a photon intensity perspective, it is not from the point of the human eye reading it.

I would propose to maybe use a different color space such as oklab or similar? They solve the muddy problem, but also stay more close to how the human eye reads gradients.

A good example of gradients in different color spaces can be found on this website: https://codepen.io/argyleink/pen/OJObWEW

You'd just have to convert the texture back from linear srgb to the output's color space once the blur is finished (sRGB for niri's case, I don't believe any others are supported).

It will look correct as long as the blending is performed in a linear color space.

The reason oklab and similar colour spaces exist is because gradients should be performed in a perceptual colour space as that better fits with designer expectations (keeping saturation, light levels constant, etc), but blending should be physically accurate instead.

@yayuuu
Copy link

yayuuu commented Mar 10, 2026

In what color space is the blur being performed? From a glance at the shaders it looks like it's plain sRGB. Wouldn't we want something like srgb_to_linear as done in border.frag so that the color blending is done in a linear color space, and then convert back to sRGB?

But srgb-linear is not really nice when looking at the human eye. It maybe avoids the muddy color in between, but also creates very strange gradients for example from white to black:

image In this case the color in the middle is already very very bright for the human eye. Even though it is "linear" from a photon intensity perspective, it is not from the point of the human eye reading it.

I would propose to maybe use a different color space such as oklab or similar? They solve the muddy problem, but also stay more close to how the human eye reads gradients.

A good example of gradients in different color spaces can be found on this website: https://codepen.io/argyleink/pen/OJObWEW

Honestly, I'd rather take something that requires the least amount of calculations. While doing more to make it look prettier is fine for blurring single image, this is a dynamic setting. If you have an animated wallpaper or desktop widgets that update in real time, doing additional calculations on a high resolution screen can be really bad for performance. I work on a PC with integrated graphics and 5120x1440 monitor that already struggles with blur during animations (going in/out of the overview, switching workspaces, etc).

@psi4j
Copy link

psi4j commented Mar 10, 2026

Maybe colorspace should be configurable if the trade-off between accuracy and performance is significant.

@evan-goode
Copy link

Upon more reading, the way to do this would be to use OpenGL's GL_SRGB8 texture format which presumably does linearization efficiently in hardware---if in fact, these textures are sRGB. But they may already be linear RGB(A), I don't know. Thought I'd ask since I noticed the colorspace math in border.frag.

@YaLTeR
Copy link
Member Author

YaLTeR commented Mar 10, 2026

This non-xray limitation is now fixed, but this (admittedly quite rare) case is currently not optimized (waiting for more fixes to the Smithay PR).

The fixes in question were implemented, so I updated this blur PR accordingly. Had to do a bit of a surgery on our wlr-screencopy and PipeWire screencast impl to support it properly, but I think it works now with no excessive perf loss from multiple clones of layer surfaces with non-xray blur in the Overview.

Please test that nothing broke, especially with regards to screencasting an output that has non-xray blur, and especially when it's also on top of a blocked-out window. Blocked-out window contents should never leak into the stream in any way, and the non-xray blur should draw without artifacts.

Re. sRGB talk: let's not complicate the math and shaders until there's an actual problem. I'm also pretty sure KDE also does it in the usual sRGB space.

@Sempyos Sempyos added area:visuals Animations, shaders, visual artifacts area:config Config parsing, default config, new settings pr kind:feature New features and functionality labels Mar 11, 2026
@w3teal
Copy link

w3teal commented Mar 15, 2026

image

Cool

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:config Config parsing, default config, new settings area:visuals Animations, shaders, visual artifacts pr kind:feature New features and functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.