From 04202423505003b1eadaf47fb53d39c1129f08be Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Fri, 25 Sep 2026 08:21:54 +0100 Subject: [PATCH] fit layer buffers during output resize --- compositor/src/winit.rs | 74 ++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/compositor/src/winit.rs b/compositor/src/winit.rs index f0dee65..22527f8 100644 --- a/compositor/src/winit.rs +++ b/compositor/src/winit.rs @@ -1452,26 +1452,30 @@ fn layer_elements( let mut elements = Vec::new(); for (geometry, layer) in layers { - elements.extend( - AsRenderElements::::render_elements::< - WaylandSurfaceRenderElement, - >( - &layer, - renderer, - geometry.loc.to_physical_precise_round(scale), - scale.into(), - 1.0, - ) - .into_iter() - .filter_map(|element| { - let origin = Point::::default(); - let element = RescaleRenderElement::from_element(element, origin, 1.0); - let element = - RelocateRenderElement::from_element(element, origin, Relocate::Relative); + let target = geometry.to_physical_precise_round(scale); + let surface_elements = AsRenderElements::::render_elements::< + WaylandSurfaceRenderElement, + >(&layer, renderer, target.loc, scale.into(), 1.0); + let source_bounds = surface_elements + .iter() + .map(|element| element.geometry(scale.into())) + .reduce(Rectangle::merge); + let content_scale = source_bounds + .map(|source| layer_content_scale(source.size, target.size)) + .unwrap_or_else(|| RenderScale::from(1.0)); + let scale_origin = source_bounds.map_or(target.loc, |source| source.loc); + let crop = target.intersection(output_crop).unwrap_or_default(); - CropRenderElement::from_element(element, scale, output_crop).map(Into::into) - }), - ); + elements.extend(surface_elements.into_iter().filter_map(|element| { + let element = RescaleRenderElement::from_element(element, scale_origin, content_scale); + let element = RelocateRenderElement::from_element( + element, + Point::::default(), + Relocate::Relative, + ); + + CropRenderElement::from_element(element, scale, crop).map(Into::into) + })); if let Some(material) = material_element(state, renderer, output, &layer, geometry) { elements.push(material); @@ -1481,6 +1485,20 @@ fn layer_elements( elements } +fn layer_content_scale( + source: Size, + target: Size, +) -> RenderScale { + if source.w <= 0 || source.h <= 0 || target.w <= 0 || target.h <= 0 { + return RenderScale::from(1.0); + } + + RenderScale::from(( + f64::from(target.w) / f64::from(source.w), + f64::from(target.h) / f64::from(source.h), + )) +} + fn material_element( state: &mut Ferese, renderer: &mut GlesRenderer, @@ -1813,7 +1831,8 @@ mod tests { use super::{ color_with_alpha, expanded_blur_region, framebuffer_capture_rect, framebuffer_clip_rect, - normalized_scale, rounded_visual_rect, scaled_visual_rect, shadow_bounds, + layer_content_scale, normalized_scale, rounded_visual_rect, scaled_visual_rect, + shadow_bounds, }; #[derive(Debug)] @@ -1866,6 +1885,21 @@ mod tests { assert_eq!(normalized_scale(f64::INFINITY), 1.0); } + #[test] + fn layer_content_tracks_new_allocation_before_the_client_repaints() { + let scale = layer_content_scale((1_000, 40).into(), (1_600, 40).into()); + + assert_eq!(scale, Scale::from((1.6, 1.0))); + } + + #[test] + fn layer_content_scale_ignores_empty_allocations() { + assert_eq!( + layer_content_scale((0, 40).into(), (1_600, 40).into()), + Scale::from(1.0) + ); + } + #[test] fn clip_rect_matches_smithay_gles_projection() { let geometry = Rectangle::::new((10, 5).into(), (30, 40).into());