render: Make blur_strength configurable

This commit is contained in:
Victoria Brekenfeld 2026-03-10 17:16:48 +01:00 committed by Victoria Brekenfeld
parent 1c5ef576f4
commit de8b47839c
11 changed files with 119 additions and 27 deletions

View file

@ -185,6 +185,7 @@ pub fn draw_surface_cursor<R>(
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
location: Point<f64, Logical>, location: Point<f64, Logical>,
scale: impl Into<Scale<f64>>, scale: impl Into<Scale<f64>>,
blur_strength: usize,
push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>), push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>),
) where ) where
R: Renderer + ImportAll + AsGlowRenderer, R: Renderer + ImportAll + AsGlowRenderer,
@ -211,6 +212,7 @@ pub fn draw_surface_cursor<R>(
1.0, 1.0,
false, false,
[0; 4], [0; 4],
blur_strength,
Kind::Cursor, Kind::Cursor,
&mut |elem| push(elem.into(), h), &mut |elem| push(elem.into(), h),
None, None,
@ -223,6 +225,7 @@ pub fn draw_dnd_icon<R>(
surface: &wl_surface::WlSurface, surface: &wl_surface::WlSurface,
location: Point<f64, Logical>, location: Point<f64, Logical>,
scale: impl Into<Scale<f64>>, scale: impl Into<Scale<f64>>,
blur_strength: usize,
push: &mut dyn FnMut(SurfaceRenderElement<R>), push: &mut dyn FnMut(SurfaceRenderElement<R>),
) where ) where
R: Renderer + ImportAll + AsGlowRenderer, R: Renderer + ImportAll + AsGlowRenderer,
@ -244,6 +247,7 @@ pub fn draw_dnd_icon<R>(
1.0, 1.0,
false, false,
[0; 4], [0; 4],
blur_strength,
FRAME_TIME_FILTER, FRAME_TIME_FILTER,
push, push,
None, None,
@ -330,6 +334,7 @@ pub fn draw_cursor<R>(
scale: Scale<f64>, scale: Scale<f64>,
buffer_scale: f64, buffer_scale: f64,
time: Time<Monotonic>, time: Time<Monotonic>,
blur_strength: usize,
draw_default: bool, draw_default: bool,
push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>), push: &mut dyn FnMut(CursorRenderElement<R>, Point<i32, Physical>),
) where ) where
@ -406,7 +411,7 @@ pub fn draw_cursor<R>(
hotspot.to_physical_precise_round(scale), hotspot.to_physical_precise_round(scale),
); );
} else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status { } else if let CursorImageStatus::Surface(ref wl_surface) = cursor_status {
draw_surface_cursor(renderer, wl_surface, location, scale, push); draw_surface_cursor(renderer, wl_surface, location, scale, blur_strength, push);
} }
} }

View file

@ -478,6 +478,7 @@ pub fn cursor_elements<'a, 'frame, R>(
seats: impl Iterator<Item = &'a Seat<State>>, seats: impl Iterator<Item = &'a Seat<State>>,
zoom_state: Option<&ZoomState>, zoom_state: Option<&ZoomState>,
theme: &Theme, theme: &Theme,
blur_strength: usize,
now: Time<Monotonic>, now: Time<Monotonic>,
output: &Output, output: &Output,
mode: CursorMode, mode: CursorMode,
@ -514,6 +515,7 @@ pub fn cursor_elements<'a, 'frame, R>(
scale.into(), scale.into(),
zoom_scale, zoom_scale,
now, now,
blur_strength,
mode != CursorMode::NotDefault, mode != CursorMode::NotDefault,
&mut |elem, hotspot| { &mut |elem, hotspot| {
push(CosmicElement::Cursor(RescaleRenderElement::from_element( push(CosmicElement::Cursor(RescaleRenderElement::from_element(
@ -538,6 +540,7 @@ pub fn cursor_elements<'a, 'frame, R>(
&dnd_icon.surface, &dnd_icon.surface,
(location + dnd_icon.offset.to_f64()).to_i32_round(), (location + dnd_icon.offset.to_f64()).to_i32_round(),
scale, scale,
blur_strength,
&mut |elem| push(CosmicElement::Dnd(elem)), &mut |elem| push(CosmicElement::Dnd(elem)),
); );
} }
@ -723,6 +726,7 @@ where
return Ok(Vec::new()); return Ok(Vec::new());
} }
let theme = shell_ref.theme().clone(); let theme = shell_ref.theme().clone();
let blur_strength = 9; // TODO
let scale = output.current_scale().fractional_scale(); let scale = output.current_scale().fractional_scale();
// we don't want to hold a shell lock across `cursor_elements`, // we don't want to hold a shell lock across `cursor_elements`,
// that is prone to deadlock with the main-thread on some grabs. // that is prone to deadlock with the main-thread on some grabs.
@ -733,6 +737,7 @@ where
seats.iter(), seats.iter(),
zoom_level, zoom_level,
&theme, &theme,
blur_strength,
now, now,
output, output,
cursor_mode, cursor_mode,
@ -845,6 +850,7 @@ where
1.0, 1.0,
false, false,
[0; 4], [0; 4],
blur_strength,
FRAME_TIME_FILTER, FRAME_TIME_FILTER,
&mut |elem| { &mut |elem| {
elements.extend( elements.extend(
@ -875,6 +881,7 @@ where
1.0, 1.0,
false, false,
[0; 4], [0; 4],
blur_strength,
FRAME_TIME_FILTER, FRAME_TIME_FILTER,
&mut |elem| { &mut |elem| {
elements.extend( elements.extend(
@ -902,6 +909,7 @@ where
1.0, 1.0,
false, false,
[0; 4], [0; 4],
blur_strength,
FRAME_TIME_FILTER, FRAME_TIME_FILTER,
&mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)),
None, None,
@ -1048,6 +1056,7 @@ fn session_lock_elements<R>(
1.0, 1.0,
false, false,
[0; 4], [0; 4],
0,
FRAME_TIME_FILTER, FRAME_TIME_FILTER,
push, push,
None, None,
@ -1507,7 +1516,19 @@ where
CosmicMappedRenderElement<R>: RenderElement<R>, CosmicMappedRenderElement<R>: RenderElement<R>,
WorkspaceRenderElement<R>: RenderElement<R>, WorkspaceRenderElement<R>: RenderElement<R>,
{ {
let elements: Vec<CosmicElement<R>> = workspace_elements( let mut elements: Vec<CosmicElement<R>> = if let Some(additional_damage) = additional_damage {
let output_geo = output.geometry().to_local(output).as_logical();
additional_damage
.into_iter()
.filter_map(|rect| rect.intersection(output_geo))
.map(DamageElement::new)
.map(CosmicElement::from)
.collect()
} else {
Vec::new()
};
elements.extend(workspace_elements(
gpu, gpu,
renderer, renderer,
shell, shell,
@ -1519,17 +1540,7 @@ where
cursor_mode, cursor_mode,
element_filter, element_filter,
None, None,
)?; )?);
if let Some(additional_damage) = additional_damage {
let output_geo = output.geometry().to_local(output).as_logical();
let additional_damage_elements: Vec<_> = additional_damage
.into_iter()
.filter_map(|rect| rect.intersection(output_geo))
.map(DamageElement::new)
.collect();
damage_tracker.damage_output(age, &additional_damage_elements)?;
}
let res = damage_tracker.render_output( let res = damage_tracker.render_output(
renderer, renderer,

View file

@ -1,7 +1,7 @@
use std::{ use std::{
borrow::{Borrow, BorrowMut}, borrow::{Borrow, BorrowMut},
sync::{ sync::{
Arc, Mutex, Arc, LazyLock, Mutex,
atomic::{AtomicBool, Ordering}, atomic::{AtomicBool, Ordering},
}, },
}; };
@ -27,6 +27,7 @@ use smithay::{
}, },
wayland::compositor::SurfaceData, wayland::compositor::SurfaceData,
}; };
use tracing::info;
use crate::{ use crate::{
backend::render::{element::AsGlowRenderer, wayland::clipped_surface::ClippingShader}, backend::render::{element::AsGlowRenderer, wayland::clipped_surface::ClippingShader},
@ -36,9 +37,50 @@ use crate::{
pub static BLUR_DOWNSAMPLE_SHADER: &str = include_str!("../shaders/blur_downsample.frag"); pub static BLUR_DOWNSAMPLE_SHADER: &str = include_str!("../shaders/blur_downsample.frag");
pub static BLUR_UPSAMPLE_SHADER: &str = include_str!("../shaders/blur_upsample.frag"); pub static BLUR_UPSAMPLE_SHADER: &str = include_str!("../shaders/blur_upsample.frag");
const PASSES: usize = 3;
const OFFSET: f64 = 1.1;
const NOISE: f32 = 0.03; const NOISE: f32 = 0.03;
const MAX_STEPS: usize = 15;
#[derive(Debug, Clone, Copy, PartialEq)]
struct BlurParameters {
passes: usize,
offset: f64,
extended_radius: i32,
}
static BLUR_PARAMS: LazyLock<Vec<BlurParameters>> = LazyLock::new(|| {
let mut params = Vec::new();
let mut remaining_steps = MAX_STEPS as isize;
let offsets = [
// min offset, max offset, extended radius to avoid artifacts
(1.0, 2.0, 10),
(2.0, 3.0, 20),
(2.0, 5.0, 50),
(3.0, 8.0, 150),
];
let sum = offsets.iter().map(|(min, max, _)| *max - *min).sum::<f64>();
for (i, (min, max, extended_radius)) in offsets.into_iter().enumerate() {
let mut iter_num = f64::ceil((max - min) / sum * (MAX_STEPS as f64)) as usize;
remaining_steps -= iter_num as isize;
if remaining_steps < 0 {
iter_num = iter_num.saturating_add_signed(remaining_steps);
}
let diff = max - min;
for j in 1..=iter_num {
params.push(BlurParameters {
passes: i + 1,
offset: min + (diff / iter_num as f64) * j as f64,
extended_radius,
});
}
}
info!("Computed blur values: {:#?}", &params);
params
});
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct BlurShaders { pub struct BlurShaders {
@ -125,6 +167,7 @@ impl BlurElement {
geometry: Rectangle<f64, Logical>, geometry: Rectangle<f64, Logical>,
output_scale: f64, output_scale: f64,
radii: [u8; 4], radii: [u8; 4],
strength: usize,
) -> Result<Option<Self>, R::Error> { ) -> Result<Option<Self>, R::Error> {
let mut blur_region_state = states.cached_state.get::<ComputedBlurRegionCachedState>(); let mut blur_region_state = states.cached_state.get::<ComputedBlurRegionCachedState>();
let Some(region) = blur_region_state.current().blur_region.as_ref() else { let Some(region) = blur_region_state.current().blur_region.as_ref() else {
@ -133,7 +176,7 @@ impl BlurElement {
let geo = geometry.to_physical_precise_round(output_scale); let geo = geometry.to_physical_precise_round(output_scale);
let mut extended_geo = geo; let mut extended_geo = geo;
let radius = OFFSET * 2.0f64.powf(PASSES as f64); let radius = BLUR_PARAMS[strength.min(MAX_STEPS - 1)].extended_radius as f64;
extended_geo.loc -= Point::<f64, Physical>::new(radius, radius); extended_geo.loc -= Point::<f64, Physical>::new(radius, radius);
extended_geo.size += Size::<f64, Physical>::new(radius, radius).upscale(2.); extended_geo.size += Size::<f64, Physical>::new(radius, radius).upscale(2.);
@ -187,6 +230,7 @@ impl BlurElement {
output_scale, output_scale,
&mut *state.lock().unwrap(), &mut *state.lock().unwrap(),
uniforms, uniforms,
strength,
)?)) )?))
} }
@ -197,19 +241,24 @@ impl BlurElement {
output_scale: f64, output_scale: f64,
state: &mut BlurState, state: &mut BlurState,
uniforms: Vec<Uniform<'static>>, uniforms: Vec<Uniform<'static>>,
strength: usize,
) -> Result<Self, R::Error> { ) -> Result<Self, R::Error> {
let renderer_id = renderer.glow_renderer().context_id(); let renderer_id = renderer.glow_renderer().context_id();
let src = geometry.size.to_buffer(output_scale, Transform::Normal); let src = geometry.size.to_buffer(output_scale, Transform::Normal);
let params = &BLUR_PARAMS[strength.min(MAX_STEPS - 1)];
let dirty = !(state let dirty = !(state
.renderer_id .renderer_id
.as_ref() .as_ref()
.is_some_and(|id| id == &renderer_id) .is_some_and(|id| id == &renderer_id)
&& state.offset == params.offset
&& state.passes == params.passes
&& &state.region == region && &state.region == region
&& state.src == src); && state.src == src);
state.renderer_id = Some(renderer_id); state.renderer_id = Some(renderer_id);
state.offset = OFFSET * output_scale; // we want the blur result to be stable across scaling; state.offset = params.offset;
state.passes = PASSES; state.passes = params.passes;
state.region = region.clone(); state.region = region.clone();
state.src = src; state.src = src;
if dirty { if dirty {

View file

@ -35,6 +35,7 @@ pub fn push_render_elements_from_surface_tree<R>(
alpha: f32, alpha: f32,
should_clip: bool, should_clip: bool,
radii: [u8; 4], radii: [u8; 4],
blur_strength: usize,
kind: impl Into<KindEvaluation>, kind: impl Into<KindEvaluation>,
push_above: &mut dyn FnMut(SurfaceRenderElement<R>), push_above: &mut dyn FnMut(SurfaceRenderElement<R>),
mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement<R>)>, mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement<R>)>,
@ -87,7 +88,12 @@ pub fn push_render_elements_from_surface_tree<R>(
) { ) {
Ok(Some(surface)) => { Ok(Some(surface)) => {
blur = BlurElement::from_surface( blur = BlurElement::from_surface(
renderer, states, geometry, scale.x, radii, renderer,
states,
geometry,
scale.x,
radii,
blur_strength,
); );
let elem: SurfaceRenderElement<R> = if radii.iter().any(|r| *r != 0) let elem: SurfaceRenderElement<R> = if radii.iter().any(|r| *r != 0)
&& should_clip && should_clip

View file

@ -668,6 +668,7 @@ impl CosmicStack {
scale, scale,
alpha, alpha,
scanout_node, scanout_node,
9, // TODO
&mut |elem| push(elem.into()), &mut |elem| push(elem.into()),
) )
}) })
@ -825,6 +826,7 @@ impl CosmicStack {
scanout_node, scanout_node,
radii.is_some(), radii.is_some(),
radii.unwrap_or([0; 4]), radii.unwrap_or([0; 4]),
9, // as TODO
&mut |elem| push_above(elem.into()), &mut |elem| push_above(elem.into()),
Some(&mut |elem| push_below(elem.into())), Some(&mut |elem| push_below(elem.into())),
); );

View file

@ -866,6 +866,7 @@ impl CosmicSurface {
scale: Scale<f64>, scale: Scale<f64>,
alpha: f32, alpha: f32,
scanout_node: Option<DrmNode>, scanout_node: Option<DrmNode>,
blur_strength: usize,
push: &mut dyn FnMut(SurfaceRenderElement<R>), push: &mut dyn FnMut(SurfaceRenderElement<R>),
) where ) where
R: Renderer + ImportAll + AsGlowRenderer, R: Renderer + ImportAll + AsGlowRenderer,
@ -889,6 +890,7 @@ impl CosmicSurface {
alpha, alpha,
false, false,
[0; 4], [0; 4],
blur_strength,
scanout_kind_eval(None, scanout_node), scanout_kind_eval(None, scanout_node),
push, push,
None, None,
@ -909,6 +911,7 @@ impl CosmicSurface {
scanout_node: Option<DrmNode>, scanout_node: Option<DrmNode>,
should_clip: bool, should_clip: bool,
radii: [u8; 4], radii: [u8; 4],
blur_strength: usize,
push_above: &mut dyn FnMut(SurfaceRenderElement<R>), push_above: &mut dyn FnMut(SurfaceRenderElement<R>),
push_below: Option<&mut dyn FnMut(SurfaceRenderElement<R>)>, push_below: Option<&mut dyn FnMut(SurfaceRenderElement<R>)>,
) where ) where
@ -931,6 +934,7 @@ impl CosmicSurface {
alpha, alpha,
should_clip, should_clip,
radii, radii,
blur_strength,
scanout_kind_eval(scanout_override, scanout_node), scanout_kind_eval(scanout_override, scanout_node),
push_above, push_above,
push_below, push_below,
@ -950,6 +954,7 @@ impl CosmicSurface {
alpha, alpha,
should_clip, should_clip,
radii, radii,
blur_strength,
scanout_kind_eval(scanout_override, scanout_node), scanout_kind_eval(scanout_override, scanout_node),
push_above, push_above,
push_below, push_below,

View file

@ -375,6 +375,7 @@ impl CosmicWindow {
scale, scale,
alpha, alpha,
scanout_node, scanout_node,
9, // TODO
&mut |elem| push(elem.into()), &mut |elem| push(elem.into()),
) )
}) })
@ -556,6 +557,7 @@ impl CosmicWindow {
scanout_node, scanout_node,
clip, clip,
radii, radii,
9, // TODO
&mut |elem| push_above(elem.into()), &mut |elem| push_above(elem.into()),
Some(&mut |elem| push_below(elem.into())), Some(&mut |elem| push_below(elem.into())),
) )

View file

@ -5408,6 +5408,8 @@ fn render_new_tree_windows<R>(
scanout_node, scanout_node,
false, false,
[0, 0, 0, 0], [0, 0, 0, 0],
// TODO
0,
&mut |elem| { &mut |elem| {
swap_elements.push(CosmicMappedRenderElement::GrabbedWindow( swap_elements.push(CosmicMappedRenderElement::GrabbedWindow(
RescaleRenderElement::from_element( RescaleRenderElement::from_element(

View file

@ -1717,6 +1717,7 @@ impl Workspace {
scanout_node, scanout_node,
false, false,
[0, 0, 0, 0], [0, 0, 0, 0],
0,
&mut fullscreen_push, &mut fullscreen_push,
None, None,
); );
@ -1905,6 +1906,7 @@ impl Workspace {
output_scale.into(), output_scale.into(),
alpha, alpha,
scanout_node, scanout_node,
0,
&mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())), &mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())),
); );
} }

View file

@ -37,6 +37,7 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) {
None, None,
false, false,
[0; 4], [0; 4],
0,
&mut |elem| elements.push(elem), &mut |elem| elements.push(elem),
None, None,
); );

View file

@ -571,6 +571,7 @@ pub fn render_window_to_buffer(
.collect(); .collect();
let shell = common.shell.read(); let shell = common.shell.read();
let blur_strength = 9; // TODO
let seat = shell.seats.last_active().clone(); let seat = shell.seats.last_active().clone();
let pointer = seat.get_pointer().unwrap(); let pointer = seat.get_pointer().unwrap();
let pointer_loc = pointer.current_location().to_i32_round().as_global(); let pointer_loc = pointer.current_location().to_i32_round().as_global();
@ -598,6 +599,7 @@ pub fn render_window_to_buffer(
1.0.into(), 1.0.into(),
1.0, 1.0,
common.clock.now(), common.clock.now(),
blur_strength,
true, true,
&mut |elem, hotspot| { &mut |elem, hotspot| {
elements.push(WindowCaptureElement::CursorElement( elements.push(WindowCaptureElement::CursorElement(
@ -619,15 +621,18 @@ pub fn render_window_to_buffer(
&dnd_icon.surface, &dnd_icon.surface,
(location + dnd_icon.offset.to_f64()).to_i32_round(), (location + dnd_icon.offset.to_f64()).to_i32_round(),
1.0, 1.0,
blur_strength,
&mut |elem| { &mut |elem| {
elements.push( elements
RelocateRenderElement::from_element( .push(
CursorRenderElement::Surface(elem), RelocateRenderElement::from_element(
Point::new(0, 0), CursorRenderElement::Surface(elem),
Relocate::Relative, Point::new(0, 0),
Relocate::Relative,
)
.into(),
) )
.into(), .into()
)
}, },
); );
} }
@ -642,6 +647,7 @@ pub fn render_window_to_buffer(
None, None,
false, false,
[0; 4], [0; 4],
blur_strength,
&mut |elem| elements.push(elem.into()), &mut |elem| elements.push(elem.into()),
None, None,
); );
@ -817,6 +823,7 @@ pub fn render_cursor_to_buffer(
1.0.into(), 1.0.into(),
1.0, 1.0,
common.clock.now(), common.clock.now(),
0,
true, true,
&mut |elem, _| { &mut |elem, _| {
elements.push( elements.push(