From b3c5821e3b9e577e79dceaadd517c873d20bf664 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Fri, 25 Sep 2026 05:16:38 +0100 Subject: [PATCH] fix: invalidate semantic materials on scene changes --- compositor/src/effects.rs | 57 ++++++++++++++++++--------- compositor/src/handlers/compositor.rs | 1 + compositor/src/state.rs | 14 +++++++ compositor/src/winit.rs | 2 + 4 files changed, 55 insertions(+), 19 deletions(-) diff --git a/compositor/src/effects.rs b/compositor/src/effects.rs index 6f1b0f4..1c2d816 100644 --- a/compositor/src/effects.rs +++ b/compositor/src/effects.rs @@ -96,11 +96,14 @@ impl SurfaceEffectsState { } } - fn set_role(&self, role: Option) { + fn set_role(&self, role: Option) -> bool { let mut current = self.role.lock().unwrap(); if *current != role { *current = role; self.generation.fetch_add(1, Ordering::Release); + true + } else { + false } } } @@ -192,7 +195,7 @@ impl Dispatch for Ferese { impl Dispatch for Ferese { fn request( - _state: &mut Self, + state: &mut Self, _client: &Client, _resource: &FereseSurfaceEffectsV1, request: ferese_surface_effects_v1::Request, @@ -220,10 +223,23 @@ impl Dispatch for Ferese { WEnum::Value(ferese_surface_effects_v1::Role::Modal) => SemanticRole::Modal, WEnum::Unknown(_) | WEnum::Value(_) => return, }; - set_surface_role(&surface, Some(role)); + if set_surface_role(&surface, Some(role)) { + state.invalidate_material_scene(); + crate::backends::direct::render_all(state); + } + } + ferese_surface_effects_v1::Request::ClearRole => { + if set_surface_role(&surface, None) { + state.invalidate_material_scene(); + crate::backends::direct::render_all(state); + } + } + ferese_surface_effects_v1::Request::Destroy => { + if detach(&surface) { + state.invalidate_material_scene(); + crate::backends::direct::render_all(state); + } } - ferese_surface_effects_v1::Request::ClearRole => set_surface_role(&surface, None), - ferese_surface_effects_v1::Request::Destroy => detach(&surface), _ => unreachable!(), } } @@ -240,21 +256,24 @@ impl Dispatch for Ferese { } } -fn set_surface_role(surface: &WlSurface, role: Option) { +fn set_surface_role(surface: &WlSurface, role: Option) -> bool { with_states(surface, |states| { - if let Some(effects) = states.data_map.get::() { - effects.set_role(role); - } - }); + states + .data_map + .get::() + .is_some_and(|effects| effects.set_role(role)) + }) } -fn detach(surface: &WlSurface) { +fn detach(surface: &WlSurface) -> bool { with_states(surface, |states| { - if let Some(effects) = states.data_map.get::() { - effects.set_role(None); - effects.attached.store(false, Ordering::Release); - } - }); + let Some(effects) = states.data_map.get::() else { + return false; + }; + let role_changed = effects.set_role(None); + let was_attached = effects.attached.swap(false, Ordering::AcqRel); + role_changed || was_attached + }) } #[cfg(test)] @@ -264,13 +283,13 @@ mod tests { #[test] fn role_changes_advance_generation_only_when_material_changes() { let effects = SurfaceEffectsState::new(); - effects.set_role(Some(SemanticRole::Panel)); + assert!(effects.set_role(Some(SemanticRole::Panel))); assert_eq!(effects.generation.load(Ordering::Acquire), 1); - effects.set_role(Some(SemanticRole::Panel)); + assert!(!effects.set_role(Some(SemanticRole::Panel))); assert_eq!(effects.generation.load(Ordering::Acquire), 1); - effects.set_role(Some(SemanticRole::Popover)); + assert!(effects.set_role(Some(SemanticRole::Popover))); assert_eq!(effects.generation.load(Ordering::Acquire), 2); } diff --git a/compositor/src/handlers/compositor.rs b/compositor/src/handlers/compositor.rs index e46a794..ab4aba5 100644 --- a/compositor/src/handlers/compositor.rs +++ b/compositor/src/handlers/compositor.rs @@ -32,6 +32,7 @@ impl CompositorHandler for Ferese { fn commit(&mut self, surface: &WlSurface) { on_commit_buffer_handler::(surface); + self.invalidate_material_scene(); if !is_sync_subsurface(surface) { let mut root = surface.clone(); while let Some(parent) = get_parent(&root) { diff --git a/compositor/src/state.rs b/compositor/src/state.rs index 68e3b2c..df8cda5 100644 --- a/compositor/src/state.rs +++ b/compositor/src/state.rs @@ -124,6 +124,7 @@ pub struct Ferese { pub(crate) rounded_clip_programs: HashMap, pub(crate) material_programs: HashMap, pub(crate) material_buffers: HashMap, + material_scene_generation: u64, closing_windows: HashMap, viewport_animations: HashMap, scrolling_world_x: HashMap, @@ -438,6 +439,7 @@ impl Ferese { rounded_clip_programs: HashMap::new(), material_programs: HashMap::new(), material_buffers: HashMap::new(), + material_scene_generation: 0, closing_windows: HashMap::new(), viewport_animations: HashMap::new(), scrolling_world_x: HashMap::new(), @@ -1276,9 +1278,21 @@ impl Ferese { } self.sync_window_stacking(); + if active_animation { + self.invalidate_material_scene(); + } + active_animation } + pub(crate) fn invalidate_material_scene(&mut self) { + self.material_scene_generation = self.material_scene_generation.wrapping_add(1); + } + + pub(crate) fn material_scene_generation(&self) -> u64 { + self.material_scene_generation + } + pub fn record_client_commit(&mut self, window: &Window) { let Some(id) = self.window_ids.get(window) else { return; diff --git a/compositor/src/winit.rs b/compositor/src/winit.rs index 252ef85..0e20dd2 100644 --- a/compositor/src/winit.rs +++ b/compositor/src/winit.rs @@ -247,6 +247,7 @@ struct MaterialParameters { tint: [f32; 4], noise: f32, generation: u64, + scene_generation: u64, opaque: bool, } @@ -1207,6 +1208,7 @@ fn material_element( tint, noise: material.noise, generation, + scene_generation: state.material_scene_generation(), opaque: material.opacity == 1.0, }; let context = renderer.context_id().erased();