From 61bef1a0a7dfc329723733065ae4e2bc7a410f0e Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Wed, 11 Mar 2026 00:03:33 +0100 Subject: [PATCH] feat(compositor): implement surface compositing and layer shell rendering Replace the clear-colour-only stub in render_output with full surface compositing via Space::render_elements_for_output. Changes: - drm_device.rs: add start_time: Instant to WeftDrmData for elapsed- time frame callbacks - drm.rs: rewrite render_output rendering block - collect SpaceRenderElements from Space via render_elements_for_output which includes both mapped windows and wlr-layer-shell surfaces from layer_map_for_output (sorted by z-index, clipped to output geometry) - pass collected elements to DrmOutput::render_frame - fix send_frame timing from Duration::ZERO to start_time.elapsed() with 16ms throttle hint - add space.refresh() and popups.cleanup() after each frame Use explicit inner block to scope space+drm borrows so post-render bookkeeping can access state mutably Cursor rendering deferred requires cursor theme loading or MemoryRenderBuffer setup; tracked separately. --- crates/weft-compositor/src/backend/drm.rs | 111 ++++++++++-------- .../weft-compositor/src/backend/drm_device.rs | 3 +- 2 files changed, 66 insertions(+), 48 deletions(-) diff --git a/crates/weft-compositor/src/backend/drm.rs b/crates/weft-compositor/src/backend/drm.rs index 51e9be8..7c0764c 100644 --- a/crates/weft-compositor/src/backend/drm.rs +++ b/crates/weft-compositor/src/backend/drm.rs @@ -5,7 +5,12 @@ pub fn run() -> anyhow::Result<()> { } #[cfg(target_os = "linux")] -use std::{collections::HashMap, path::Path, sync::Arc, time::Duration}; +use std::{ + collections::HashMap, + path::Path, + sync::Arc, + time::{Duration, Instant}, +}; #[cfg(target_os = "linux")] use anyhow::Context; @@ -71,6 +76,7 @@ const SUPPORTED_FORMATS: &[Fourcc] = &[ const SUPPORTED_FORMATS_8BIT_ONLY: &[Fourcc] = &[Fourcc::Abgr8888, Fourcc::Argb8888]; #[cfg(target_os = "linux")] +#[allow(dead_code)] type WeftMultiRenderer<'a> = MultiRenderer< 'a, 'a, @@ -239,6 +245,7 @@ pub fn run() -> anyhow::Result<()> { devices: HashMap::new(), keyboards: Vec::new(), display_handle, + start_time: Instant::now(), }); let existing: Vec<(DrmNode, std::path::PathBuf)> = state @@ -581,63 +588,73 @@ fn render_output(state: &mut WeftCompositorState, node: DrmNode, crtc: crtc::Han .unwrap_or(d.primary_gpu) }; - let WeftCompositorState { - ref mut drm, - ref space, - .. - } = *state; + let elapsed = state + .drm + .as_ref() + .map(|d| d.start_time.elapsed()) + .unwrap_or_default(); - let drm_data = match drm.as_mut() { - Some(d) => d, - None => return, - }; + { + let WeftCompositorState { + ref mut drm, + ref space, + .. + } = *state; - let WeftDrmData { - ref mut gpu_manager, - ref mut devices, - .. - } = *drm_data; + let drm_data = match drm.as_mut() { + Some(d) => d, + None => return, + }; - let device = match devices.get_mut(&node) { - Some(d) => d, - None => return, - }; - let surface = match device.surfaces.get_mut(&crtc) { - Some(s) => s, - None => return, - }; + let WeftDrmData { + ref mut gpu_manager, + ref mut devices, + .. + } = *drm_data; - let mut renderer = match gpu_manager.single_renderer(&render_node) { - Ok(r) => r, - Err(e) => { - tracing::warn!(?e, "renderer unavailable"); - return; - } - }; + let device = match devices.get_mut(&node) { + Some(d) => d, + None => return, + }; + let surface = match device.surfaces.get_mut(&crtc) { + Some(s) => s, + None => return, + }; - // Wave 2: clear-colour-only frame to establish the VBlank pipeline. - // Surface compositing is added in Wave 3. - let elements: &[WaylandSurfaceRenderElement>] = &[]; - match surface.drm_output.render_frame( - &mut renderer, - elements, - [0.08_f32, 0.08, 0.08, 1.0], - FrameFlags::DEFAULT, - ) { - Ok(result) if !result.is_empty => { - if let Err(e) = surface.drm_output.queue_frame(None) { - tracing::warn!(?e, "queue_frame failed"); + let mut renderer = match gpu_manager.single_renderer(&render_node) { + Ok(r) => r, + Err(e) => { + tracing::warn!(?e, "renderer unavailable"); + return; } + }; + + let elements = space + .render_elements_for_output(&mut renderer, &output, 1.0) + .unwrap_or_default(); + + match surface.drm_output.render_frame( + &mut renderer, + &elements, + [0.08_f32, 0.08, 0.08, 1.0], + FrameFlags::DEFAULT, + ) { + Ok(result) if !result.is_empty => { + if let Err(e) = surface.drm_output.queue_frame(None) { + tracing::warn!(?e, "queue_frame failed"); + } + } + Ok(_) => {} + Err(e) => tracing::warn!(?e, "render_frame failed"), } - Ok(_) => {} - Err(e) => tracing::warn!(?e, "render_frame failed"), } - space.elements().for_each(|window| { - window.send_frame(&output, Duration::ZERO, Some(Duration::ZERO), |_, _| { + state.space.elements().for_each(|window| { + window.send_frame(&output, elapsed, Some(Duration::from_millis(16)), |_, _| { Some(output.clone()) }); }); - + state.space.refresh(); + state.popups.cleanup(); let _ = state.display_handle.flush_clients(); } diff --git a/crates/weft-compositor/src/backend/drm_device.rs b/crates/weft-compositor/src/backend/drm_device.rs index efc1e64..67fe916 100644 --- a/crates/weft-compositor/src/backend/drm_device.rs +++ b/crates/weft-compositor/src/backend/drm_device.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, time::Instant}; use smithay::{ backend::{ @@ -55,4 +55,5 @@ pub struct WeftDrmData { pub devices: HashMap, pub keyboards: Vec, pub display_handle: DisplayHandle, + pub start_time: Instant, }