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.
This commit is contained in:
Marco Allegretti 2026-03-11 00:03:33 +01:00
parent 732e572c43
commit 61bef1a0a7
2 changed files with 66 additions and 48 deletions

View file

@ -5,7 +5,12 @@ pub fn run() -> anyhow::Result<()> {
} }
#[cfg(target_os = "linux")] #[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")] #[cfg(target_os = "linux")]
use anyhow::Context; use anyhow::Context;
@ -71,6 +76,7 @@ const SUPPORTED_FORMATS: &[Fourcc] = &[
const SUPPORTED_FORMATS_8BIT_ONLY: &[Fourcc] = &[Fourcc::Abgr8888, Fourcc::Argb8888]; const SUPPORTED_FORMATS_8BIT_ONLY: &[Fourcc] = &[Fourcc::Abgr8888, Fourcc::Argb8888];
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
#[allow(dead_code)]
type WeftMultiRenderer<'a> = MultiRenderer< type WeftMultiRenderer<'a> = MultiRenderer<
'a, 'a,
'a, 'a,
@ -239,6 +245,7 @@ pub fn run() -> anyhow::Result<()> {
devices: HashMap::new(), devices: HashMap::new(),
keyboards: Vec::new(), keyboards: Vec::new(),
display_handle, display_handle,
start_time: Instant::now(),
}); });
let existing: Vec<(DrmNode, std::path::PathBuf)> = state 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) .unwrap_or(d.primary_gpu)
}; };
let WeftCompositorState { let elapsed = state
ref mut drm, .drm
ref space, .as_ref()
.. .map(|d| d.start_time.elapsed())
} = *state; .unwrap_or_default();
let drm_data = match drm.as_mut() { {
Some(d) => d, let WeftCompositorState {
None => return, ref mut drm,
}; ref space,
..
} = *state;
let WeftDrmData { let drm_data = match drm.as_mut() {
ref mut gpu_manager, Some(d) => d,
ref mut devices, None => return,
.. };
} = *drm_data;
let device = match devices.get_mut(&node) { let WeftDrmData {
Some(d) => d, ref mut gpu_manager,
None => return, ref mut devices,
}; ..
let surface = match device.surfaces.get_mut(&crtc) { } = *drm_data;
Some(s) => s,
None => return,
};
let mut renderer = match gpu_manager.single_renderer(&render_node) { let device = match devices.get_mut(&node) {
Ok(r) => r, Some(d) => d,
Err(e) => { None => return,
tracing::warn!(?e, "renderer unavailable"); };
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. let mut renderer = match gpu_manager.single_renderer(&render_node) {
// Surface compositing is added in Wave 3. Ok(r) => r,
let elements: &[WaylandSurfaceRenderElement<WeftMultiRenderer<'_>>] = &[]; Err(e) => {
match surface.drm_output.render_frame( tracing::warn!(?e, "renderer unavailable");
&mut renderer, return;
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 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| { state.space.elements().for_each(|window| {
window.send_frame(&output, Duration::ZERO, Some(Duration::ZERO), |_, _| { window.send_frame(&output, elapsed, Some(Duration::from_millis(16)), |_, _| {
Some(output.clone()) Some(output.clone())
}); });
}); });
state.space.refresh();
state.popups.cleanup();
let _ = state.display_handle.flush_clients(); let _ = state.display_handle.flush_clients();
} }

View file

@ -1,4 +1,4 @@
use std::collections::HashMap; use std::{collections::HashMap, time::Instant};
use smithay::{ use smithay::{
backend::{ backend::{
@ -55,4 +55,5 @@ pub struct WeftDrmData {
pub devices: HashMap<DrmNode, WeftDrmDevice>, pub devices: HashMap<DrmNode, WeftDrmDevice>,
pub keyboards: Vec<smithay::reexports::input::Device>, pub keyboards: Vec<smithay::reexports::input::Device>,
pub display_handle: DisplayHandle, pub display_handle: DisplayHandle,
pub start_time: Instant,
} }