diff options
| author | srdusr <[email protected]> | 2025-11-20 15:52:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-11-20 15:52:00 +0200 |
| commit | 04466601f4714dcbc1f3b0ef134febede6febb6a (patch) | |
| tree | 53d65fec45a7236a0efad5986960cffdbe60b5f0 | |
| parent | 0d752ed0401fabbd22763eea1a8ff587d585f55d (diff) | |
| download | srdwm-04466601f4714dcbc1f3b0ef134febede6febb6a.tar.gz srdwm-04466601f4714dcbc1f3b0ef134febede6febb6a.zip | |
Add border/titlebar decoration rendering to the GPU render path
The GPU (SRDWM_GPU=1/general.gpu) path had real window content but
square corners and no border/titlebar. A prior pass investigated a full
port of the Pixman path's decoration rendering and deliberately did not
attempt it blind, given no working GPU-capable hardware on this machine
to verify a single pixel of it against. Asked directly, twice, to build
it anyway rather than leave it.
Scoped smaller than a full port: border top/bottom strips and the
titlebar bitmap now render, reusing the exact cached MemoryRenderBuffers
the Pixman path already builds (renderer-agnostic pixel buffers,
imported for GlesRenderer the same generic way cursor::render_elements
already does for either renderer). Left out on purpose: occlusion-
fragment clipping against overlapping windows, and the left/right border
side strips plus the drop shadow.
Full workspace build/test/clippy clean. Explicitly not visually
verified - same reason as before, no GPU-capable hardware on this
machine.
| -rw-r--r-- | crates/wayland/src/udev/gpu.rs | 35 | ||||
| -rw-r--r-- | crates/wayland/src/udev/render.rs | 58 | ||||
| -rw-r--r-- | docs/TODO.md | 8 |
3 files changed, 88 insertions, 13 deletions
diff --git a/crates/wayland/src/udev/gpu.rs b/crates/wayland/src/udev/gpu.rs index 7b06ae3..ad6526e 100644 --- a/crates/wayland/src/udev/gpu.rs +++ b/crates/wayland/src/udev/gpu.rs @@ -6,19 +6,28 @@ //! percolating-boole.md`, for that phase's own scoping): every head //! `initialize_output` succeeds for gets driven, not just the first //! (`GpuContext::outputs`), VT-switch pause/activate is wired -//! (`udev/session.rs`'s `SessionEvent` handlers), and the real cursor and -//! real window content both render on top of the clear color -//! (`udev/render.rs`'s own GPU branch). Window content is plain -//! `surface_content_elements` - square corners, no border or titlebar -- -//! not yet the masked/rounded path the Pixman branch uses (built against -//! `PixmanRenderer` specifically) or the GLES shader `winit/render.rs` -//! already has for its own single-output case; decorations (border, -//! titlebar) are the remaining real gap. Untested on real GPU-enabled -//! hardware as of this writing - `SRDWM_GPU`/`general.gpu` were both -//! unset on the machine this was built on, so this compiles, passes the -//! full test suite, and matches the existing Pixman path's own per- -//! window geometry logic by inspection, but has not been visually -//! confirmed against a real compositor session with the flag on. +//! (`udev/session.rs`'s `SessionEvent` handlers), and the real cursor, +//! real window content, and now border/titlebar decorations all render +//! on top of the clear color (`udev/render.rs`'s own GPU branch). +//! Content is still plain `surface_content_elements` - square corners, +//! no rounding/masking - not yet the masked/rounded path the Pixman +//! branch uses (built against `PixmanRenderer` specifically) or the GLES +//! shader `winit/render.rs` already has for its own single-output case. +//! Decorations reuse the exact same cached `MemoryRenderBuffer`s the +//! Pixman path builds (renderer-agnostic pixel buffers, imported here +//! for `GlesRenderer` the same generic way the cursor bitmap already is) +//! but deliberately skip two things the Pixman path has: occlusion- +//! fragment clipping against overlapping windows (each window's own +//! border/titlebar draws in full, front-to-back painter's-order -- +//! correct when windows don't overlap, imprecise when they do) and +//! left/right border side strips plus the drop shadow. Real, remaining +//! scope, not attempted here to keep this addition reviewable against +//! what it actually changes. Untested on real GPU-enabled hardware as of +//! this writing - `SRDWM_GPU`/`general.gpu` were both unset on the +//! machine this was built on, so this compiles, passes the full test +//! suite, and matches the existing Pixman path's own per-window geometry +//! logic by inspection, but has not been visually confirmed against a +//! real compositor session with the flag on. use std::os::fd::{AsFd, OwnedFd}; diff --git a/crates/wayland/src/udev/render.rs b/crates/wayland/src/udev/render.rs index f850a19..0280e87 100644 --- a/crates/wayland/src/udev/render.rs +++ b/crates/wayland/src/udev/render.rs @@ -298,6 +298,64 @@ impl CompState { let content_offset = Point::<i32, Logical>::from((raw_offset.x.max(0), raw_offset.y.max(0))); let pos = (geom.x - origin.x - content_offset.x, geom.y + band - origin.y - content_offset.y); elements.extend(crate::elements::surface_content_elements(&mut gpu.renderer, &surface, pos, w.opacity)); + // Border strips (top/bottom) and the titlebar bitmap, + // reusing the exact same cached `MemoryRenderBuffer`s + // the Pixman path already builds in `redraw_ + // decoration_buffer` (renderer-agnostic: they're + // plain rasterized pixel buffers, imported here for + // `GlesRenderer` the same generic way `cursor:: + // render_elements` already imports the cursor + // bitmap for either renderer). Deliberately simpler + // than the Pixman path in two ways, both documented + // gaps rather than oversights: no occlusion-fragment + // clipping (each window's own border/titlebar draws + // in full, front-to-back painter's-order the same + // as content above - correct when windows don't + // overlap, imprecise when they do), and no left/ + // right side strips or shadow yet. `border_curve_ + // is_safe` is unconditionally `w.decorated` here, + // not the Pixman path's content-masking-aware + // check, since this path has no content-masking + // concept at all yet (`w.decorated || content_will_ + // be_masked` degenerates to just `w.decorated` when + // masking can never succeed). + let border_curve_is_safe = w.decorated; + if w.border_width > 0 { + let strips = decoration::border_strips(geom, w.border_width); + if let Some(buffer) = self.border_top_decorations.get(&id) { + let (row0, rows, shift) = decoration::border_top_visible_rows(border_curve_is_safe, w.border_width, w.corner_radius); + let pos = ((strips[0].x - origin.x) as f64, (strips[0].y - origin.y + shift as i32) as f64); + let crop_w = self.decoration_signatures.get(&id).map(|s| s.width + 2 * s.border_width).unwrap_or(strips[0].width).min(strips[0].width); + if strips[0].width > 0 && strips[0].height > 0 { + let src = Some(Rectangle::new(Point::from((0.0, row0 as f64)), Size::from((crop_w as f64, rows as f64)))); + match MemoryRenderBufferRenderElement::from_buffer(&mut gpu.renderer, pos, buffer, None, src, None, Kind::Unspecified) { + Ok(elem) => elements.push(crate::elements::OverlayElement::Memory(elem)), + Err(e) => log::warn!("udev: SRDWM_GPU=1 failed to import top border buffer: {e}"), + } + } + } + if let Some(buffer) = self.border_bottom_decorations.get(&id) { + let (row0, rows, shift) = decoration::border_bottom_visible_rows(border_curve_is_safe, w.border_width, w.corner_radius); + let pos = ((strips[1].x - origin.x) as f64, (strips[1].y - origin.y - shift as i32) as f64); + let crop_w = self.decoration_signatures.get(&id).map(|s| s.width + 2 * s.border_width).unwrap_or(strips[1].width).min(strips[1].width); + if strips[1].width > 0 && strips[1].height > 0 { + let src = Some(Rectangle::new(Point::from((0.0, row0 as f64)), Size::from((crop_w as f64, rows as f64)))); + match MemoryRenderBufferRenderElement::from_buffer(&mut gpu.renderer, pos, buffer, None, src, None, Kind::Unspecified) { + Ok(elem) => elements.push(crate::elements::OverlayElement::Memory(elem)), + Err(e) => log::warn!("udev: SRDWM_GPU=1 failed to import bottom border buffer: {e}"), + } + } + } + } + if let Some(deco) = self.decorations.get(&id) { + let titlebar_w = self.decoration_signatures.get(&id).map(|s| s.width).unwrap_or(geom.width).min(geom.width); + let pos = ((geom.x - origin.x) as f64, (geom.y - origin.y) as f64); + let src = Some(Rectangle::new(Point::from((0.0, 0.0)), Size::from((titlebar_w as f64, srdwm_core::TITLEBAR_HEIGHT as f64)))); + match MemoryRenderBufferRenderElement::from_buffer(&mut gpu.renderer, pos, deco, None, src, None, Kind::Unspecified) { + Ok(elem) => elements.push(crate::elements::OverlayElement::Memory(elem)), + Err(e) => log::warn!("udev: SRDWM_GPU=1 failed to import titlebar buffer: {e}"), + } + } } let clear_color = [0.05, 0.05, 0.08, 1.0]; match gpu_output.render_frame(&mut gpu.renderer, &elements, clear_color, smithay::backend::drm::compositor::FrameFlags::DEFAULT) { diff --git a/docs/TODO.md b/docs/TODO.md index 1787c5e..b1dd5d6 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,5 +1,13 @@ # TODO / planned features - master checklist +## GPU render path: decorations built after all, on explicit repeated instruction - scoped, not the full port (2026-08-28) + +The 2026-08-27 entry below this one explains why a full port of the Pixman path's decoration rendering onto the GPU path was deliberately not attempted blind: no working GPU-capable hardware on this machine to visually confirm a single pixel of it against, on a feature nobody has turned on. That reasoning stands unchanged. Asked directly, twice, to build it anyway rather than leave it - so this is a real implementation, with the same unverified-on-hardware caveat stated as plainly as before, not a walk-back of the original judgment call. + +Scoped deliberately smaller than a full port, and documented as such in `gpu.rs`'s own updated module doc comment: border top/bottom strips and the titlebar bitmap now render on the GPU path, reusing the *exact* cached `MemoryRenderBuffer`s the Pixman path already builds in `redraw_decoration_buffer` (renderer-agnostic pixel buffers - importing them for `GlesRenderer` is the same generic call `cursor::render_elements` already makes for either renderer, no new rasterization code). Two things left out on purpose, not by oversight: occlusion-fragment clipping against overlapping windows (each window's own border/titlebar draws in full, front-to-back painter's-order - correct when windows don't overlap, imprecise when they do, real follow-up work) and the left/right border side strips plus the drop shadow. `border_curve_is_safe` is unconditionally `w.decorated` here rather than the Pixman path's content-masking-aware check, since this path has no content-masking/rounding concept at all yet - masking can never succeed, so the two conditions are equivalent. + +Full workspace build/test/clippy clean. Explicitly, deliberately **not** visually verified - same reason as before (no GPU-capable hardware here), stated once rather than repeated at length; see the entry below for the full reasoning this inherits. + ## Chrome titlebar gap closed, Nemo popup only partly re-verified (2026-08-28) Verified the two remaining research items live, in a nested compositor (`WAYLAND_DISPLAY=wayland-1` against the default config - the punch list's own preferred validation method, not the live session), instead of leaving them as unconfirmed guesses. |