diff --git a/src/backend/render/cursor.rs b/src/backend/render/cursor.rs index e43d99e5..360991de 100644 --- a/src/backend/render/cursor.rs +++ b/src/backend/render/cursor.rs @@ -185,6 +185,7 @@ pub fn draw_surface_cursor( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, + blur_strength: usize, push: &mut dyn FnMut(CursorRenderElement, Point), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -211,6 +212,7 @@ pub fn draw_surface_cursor( 1.0, false, [0; 4], + blur_strength, Kind::Cursor, &mut |elem| push(elem.into(), h), None, @@ -223,6 +225,7 @@ pub fn draw_dnd_icon( surface: &wl_surface::WlSurface, location: Point, scale: impl Into>, + blur_strength: usize, push: &mut dyn FnMut(SurfaceRenderElement), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -244,6 +247,7 @@ pub fn draw_dnd_icon( 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, push, None, @@ -330,6 +334,7 @@ pub fn draw_cursor( scale: Scale, buffer_scale: f64, time: Time, + blur_strength: usize, draw_default: bool, push: &mut dyn FnMut(CursorRenderElement, Point), ) where @@ -406,7 +411,7 @@ pub fn draw_cursor( hotspot.to_physical_precise_round(scale), ); } 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); } } diff --git a/src/backend/render/mod.rs b/src/backend/render/mod.rs index a22289f4..7c1c1404 100644 --- a/src/backend/render/mod.rs +++ b/src/backend/render/mod.rs @@ -478,6 +478,7 @@ pub fn cursor_elements<'a, 'frame, R>( seats: impl Iterator>, zoom_state: Option<&ZoomState>, theme: &Theme, + blur_strength: usize, now: Time, output: &Output, mode: CursorMode, @@ -514,6 +515,7 @@ pub fn cursor_elements<'a, 'frame, R>( scale.into(), zoom_scale, now, + blur_strength, mode != CursorMode::NotDefault, &mut |elem, hotspot| { push(CosmicElement::Cursor(RescaleRenderElement::from_element( @@ -538,6 +540,7 @@ pub fn cursor_elements<'a, 'frame, R>( &dnd_icon.surface, (location + dnd_icon.offset.to_f64()).to_i32_round(), scale, + blur_strength, &mut |elem| push(CosmicElement::Dnd(elem)), ); } @@ -723,6 +726,7 @@ where return Ok(Vec::new()); } let theme = shell_ref.theme().clone(); + let blur_strength = 9; // TODO let scale = output.current_scale().fractional_scale(); // we don't want to hold a shell lock across `cursor_elements`, // that is prone to deadlock with the main-thread on some grabs. @@ -733,6 +737,7 @@ where seats.iter(), zoom_level, &theme, + blur_strength, now, output, cursor_mode, @@ -845,6 +850,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| { elements.extend( @@ -875,6 +881,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| { elements.extend( @@ -902,6 +909,7 @@ where 1.0, false, [0; 4], + blur_strength, FRAME_TIME_FILTER, &mut |elem| elements.extend(crop_to_output(elem.into()).map(Into::into)), None, @@ -1048,6 +1056,7 @@ fn session_lock_elements( 1.0, false, [0; 4], + 0, FRAME_TIME_FILTER, push, None, @@ -1507,7 +1516,19 @@ where CosmicMappedRenderElement: RenderElement, WorkspaceRenderElement: RenderElement, { - let elements: Vec> = workspace_elements( + let mut elements: Vec> = 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, renderer, shell, @@ -1519,17 +1540,7 @@ where cursor_mode, element_filter, 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( renderer, diff --git a/src/backend/render/wayland/blur_effect.rs b/src/backend/render/wayland/blur_effect.rs index 895cd19e..2a0be977 100644 --- a/src/backend/render/wayland/blur_effect.rs +++ b/src/backend/render/wayland/blur_effect.rs @@ -1,7 +1,7 @@ use std::{ borrow::{Borrow, BorrowMut}, sync::{ - Arc, Mutex, + Arc, LazyLock, Mutex, atomic::{AtomicBool, Ordering}, }, }; @@ -27,6 +27,7 @@ use smithay::{ }, wayland::compositor::SurfaceData, }; +use tracing::info; use crate::{ 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_UPSAMPLE_SHADER: &str = include_str!("../shaders/blur_upsample.frag"); -const PASSES: usize = 3; -const OFFSET: f64 = 1.1; 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> = 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::(); + 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: {:#?}", ¶ms); + params +}); #[derive(Debug, Clone)] pub struct BlurShaders { @@ -125,6 +167,7 @@ impl BlurElement { geometry: Rectangle, output_scale: f64, radii: [u8; 4], + strength: usize, ) -> Result, R::Error> { let mut blur_region_state = states.cached_state.get::(); 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 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::::new(radius, radius); extended_geo.size += Size::::new(radius, radius).upscale(2.); @@ -187,6 +230,7 @@ impl BlurElement { output_scale, &mut *state.lock().unwrap(), uniforms, + strength, )?)) } @@ -197,19 +241,24 @@ impl BlurElement { output_scale: f64, state: &mut BlurState, uniforms: Vec>, + strength: usize, ) -> Result { let renderer_id = renderer.glow_renderer().context_id(); let src = geometry.size.to_buffer(output_scale, Transform::Normal); + let params = &BLUR_PARAMS[strength.min(MAX_STEPS - 1)]; let dirty = !(state .renderer_id .as_ref() .is_some_and(|id| id == &renderer_id) + && state.offset == params.offset + && state.passes == params.passes && &state.region == region && state.src == src); + state.renderer_id = Some(renderer_id); - state.offset = OFFSET * output_scale; // we want the blur result to be stable across scaling; - state.passes = PASSES; + state.offset = params.offset; + state.passes = params.passes; state.region = region.clone(); state.src = src; if dirty { diff --git a/src/backend/render/wayland/mod.rs b/src/backend/render/wayland/mod.rs index b9e07a80..9ac1109c 100644 --- a/src/backend/render/wayland/mod.rs +++ b/src/backend/render/wayland/mod.rs @@ -35,6 +35,7 @@ pub fn push_render_elements_from_surface_tree( alpha: f32, should_clip: bool, radii: [u8; 4], + blur_strength: usize, kind: impl Into, push_above: &mut dyn FnMut(SurfaceRenderElement), mut push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, @@ -87,7 +88,12 @@ pub fn push_render_elements_from_surface_tree( ) { Ok(Some(surface)) => { blur = BlurElement::from_surface( - renderer, states, geometry, scale.x, radii, + renderer, + states, + geometry, + scale.x, + radii, + blur_strength, ); let elem: SurfaceRenderElement = if radii.iter().any(|r| *r != 0) && should_clip diff --git a/src/shell/element/stack.rs b/src/shell/element/stack.rs index 58e28753..0980facb 100644 --- a/src/shell/element/stack.rs +++ b/src/shell/element/stack.rs @@ -668,6 +668,7 @@ impl CosmicStack { scale, alpha, scanout_node, + 9, // TODO &mut |elem| push(elem.into()), ) }) @@ -825,6 +826,7 @@ impl CosmicStack { scanout_node, radii.is_some(), radii.unwrap_or([0; 4]), + 9, // as TODO &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), ); diff --git a/src/shell/element/surface.rs b/src/shell/element/surface.rs index 53c3d4bd..4a88d2df 100644 --- a/src/shell/element/surface.rs +++ b/src/shell/element/surface.rs @@ -866,6 +866,7 @@ impl CosmicSurface { scale: Scale, alpha: f32, scanout_node: Option, + blur_strength: usize, push: &mut dyn FnMut(SurfaceRenderElement), ) where R: Renderer + ImportAll + AsGlowRenderer, @@ -889,6 +890,7 @@ impl CosmicSurface { alpha, false, [0; 4], + blur_strength, scanout_kind_eval(None, scanout_node), push, None, @@ -909,6 +911,7 @@ impl CosmicSurface { scanout_node: Option, should_clip: bool, radii: [u8; 4], + blur_strength: usize, push_above: &mut dyn FnMut(SurfaceRenderElement), push_below: Option<&mut dyn FnMut(SurfaceRenderElement)>, ) where @@ -931,6 +934,7 @@ impl CosmicSurface { alpha, should_clip, radii, + blur_strength, scanout_kind_eval(scanout_override, scanout_node), push_above, push_below, @@ -950,6 +954,7 @@ impl CosmicSurface { alpha, should_clip, radii, + blur_strength, scanout_kind_eval(scanout_override, scanout_node), push_above, push_below, diff --git a/src/shell/element/window.rs b/src/shell/element/window.rs index 48949127..de949b8e 100644 --- a/src/shell/element/window.rs +++ b/src/shell/element/window.rs @@ -375,6 +375,7 @@ impl CosmicWindow { scale, alpha, scanout_node, + 9, // TODO &mut |elem| push(elem.into()), ) }) @@ -556,6 +557,7 @@ impl CosmicWindow { scanout_node, clip, radii, + 9, // TODO &mut |elem| push_above(elem.into()), Some(&mut |elem| push_below(elem.into())), ) diff --git a/src/shell/layout/tiling/mod.rs b/src/shell/layout/tiling/mod.rs index fbcec105..3a3633db 100644 --- a/src/shell/layout/tiling/mod.rs +++ b/src/shell/layout/tiling/mod.rs @@ -5408,6 +5408,8 @@ fn render_new_tree_windows( scanout_node, false, [0, 0, 0, 0], + // TODO + 0, &mut |elem| { swap_elements.push(CosmicMappedRenderElement::GrabbedWindow( RescaleRenderElement::from_element( diff --git a/src/shell/workspace.rs b/src/shell/workspace.rs index b8b104c5..dc5d9f63 100644 --- a/src/shell/workspace.rs +++ b/src/shell/workspace.rs @@ -1717,6 +1717,7 @@ impl Workspace { scanout_node, false, [0, 0, 0, 0], + 0, &mut fullscreen_push, None, ); @@ -1905,6 +1906,7 @@ impl Workspace { output_scale.into(), alpha, scanout_node, + 0, &mut |elem| push(WorkspaceRenderElement::FullscreenPopup(elem.into())), ); } diff --git a/src/utils/screenshot.rs b/src/utils/screenshot.rs index eb7730c7..de544b72 100644 --- a/src/utils/screenshot.rs +++ b/src/utils/screenshot.rs @@ -37,6 +37,7 @@ pub fn screenshot_window(state: &mut State, surface: &CosmicSurface) { None, false, [0; 4], + 0, &mut |elem| elements.push(elem), None, ); diff --git a/src/wayland/handlers/image_copy_capture/render.rs b/src/wayland/handlers/image_copy_capture/render.rs index 4b249a36..9018f8ce 100644 --- a/src/wayland/handlers/image_copy_capture/render.rs +++ b/src/wayland/handlers/image_copy_capture/render.rs @@ -571,6 +571,7 @@ pub fn render_window_to_buffer( .collect(); let shell = common.shell.read(); + let blur_strength = 9; // TODO let seat = shell.seats.last_active().clone(); let pointer = seat.get_pointer().unwrap(); 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, common.clock.now(), + blur_strength, true, &mut |elem, hotspot| { elements.push(WindowCaptureElement::CursorElement( @@ -619,15 +621,18 @@ pub fn render_window_to_buffer( &dnd_icon.surface, (location + dnd_icon.offset.to_f64()).to_i32_round(), 1.0, + blur_strength, &mut |elem| { - elements.push( - RelocateRenderElement::from_element( - CursorRenderElement::Surface(elem), - Point::new(0, 0), - Relocate::Relative, + elements + .push( + RelocateRenderElement::from_element( + CursorRenderElement::Surface(elem), + Point::new(0, 0), + Relocate::Relative, + ) + .into(), ) - .into(), - ) + .into() }, ); } @@ -642,6 +647,7 @@ pub fn render_window_to_buffer( None, false, [0; 4], + blur_strength, &mut |elem| elements.push(elem.into()), None, ); @@ -817,6 +823,7 @@ pub fn render_cursor_to_buffer( 1.0.into(), 1.0, common.clock.now(), + 0, true, &mut |elem, _| { elements.push(