diff options
| author | srdusr <[email protected]> | 2025-08-13 20:10:00 +0200 |
|---|---|---|
| committer | srdusr <[email protected]> | 2025-08-13 20:10:00 +0200 |
| commit | 6694a2d9946d98d5752d84b22c45879cab5c9abe (patch) | |
| tree | 49fcbeec0c06573400195eadfdc9bfb222d9c8fd /crates | |
| parent | 7f4740046a004a1ad06aefa5d1d6b1a351571b1a (diff) | |
| download | srdwm-6694a2d9946d98d5752d84b22c45879cab5c9abe.tar.gz srdwm-6694a2d9946d98d5752d84b22c45879cab5c9abe.zip | |
Fix terminal content disappearing on resize: don't cache a blank content mask
Reported live: "terminal output/everything disappears when i sometimes
resize terminal." masked_content_buffer (the udev/Pixman rounded-corner
content-masking path, live on this machine via general.rounded_corners)
rendered a window's whole surface tree into an off-screen buffer and
returned Some(bytes) unconditionally, with no check for whether that
tree actually produced any drawable elements.
content_epoch bumps on every commit, and a fast interactive resize is a
rapid-fire sequence of commits - real odds that one races ahead of the
client's own texture import, making the off-screen render legitimately
come back empty. That blank result got returned as Some and cached under
the new epoch the same as a correct one would, and rounded_content_buffer
only rebuilds on the next epoch change - so the blank buffer stayed on
screen, fully transparent, until the window's next real content change,
indefinite for an idle terminal.
masked_content_buffer now returns None when the element tree is empty,
before doing the render+readback at all - the same "give up unmasked"
pattern already used for a genuine renderer error. rounded_content_buffer
drops rather than replaces its cache entry on None, so the render loop
falls back to unmasked content for that one frame and retries the masked
path on the next.
Scoped to the udev/Pixman backend; winit masks via a GLES shader with no
equivalent failure mode.
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/wayland/src/rounded_corners_pixman.rs | 24 |
1 files changed, 24 insertions, 0 deletions
diff --git a/crates/wayland/src/rounded_corners_pixman.rs b/crates/wayland/src/rounded_corners_pixman.rs index 808d0fe..a0126fe 100644 --- a/crates/wayland/src/rounded_corners_pixman.rs +++ b/crates/wayland/src/rounded_corners_pixman.rs @@ -92,6 +92,30 @@ pub(crate) fn masked_content_buffer(renderer: &mut PixmanRenderer, surface: &WlS // transparency so the border/desktop already drawn underneath on the // real output shows through there, not a solid black patch. let elements = render_elements_from_surface_tree::<_, crate::elements::OverlayElement<PixmanRenderer>>(renderer, surface, loc, 1.0, 1.0, Kind::Unspecified); + // An empty tree here is not trusted as "this window genuinely has no + // content" - it's indistinguishable from a transient race where a + // commit has already bumped `content_epoch` (every commit does, + // unconditionally) but the surface's own texture import hasn't caught + // up yet, real and reachable during a fast resize's rapid-fire commits. + // `rounded_content_buffer`'s cache keys purely on `epoch`/`radius`/ + // `loc`/`size`, not on content, so a blank render returned as `Some` + // here would get cached as this window's current, correct content -- + // and then persist on screen, fully transparent, until the *next* + // epoch-bumping commit, which for an idle/settled terminal can be a + // long wait. Reported live as a terminal's content disappearing + // entirely on some resizes. Returning `None` instead makes the caller + // treat this the same as any other failed render: `rounded_content_ + // buffer` drops any cached entry rather than replacing it with this + // blank one, and the render loop falls back to the ordinary unmasked + // `surface_content_elements` path for this one frame - which shows + // the window's real content (square corners for a frame, not blank), + // and naturally retries the masked path again next frame since the + // dropped cache entry reads as stale again with nothing to compare + // against. + if elements.is_empty() { + log::debug!("rounded_corners_pixman: masked_content_buffer: surface tree had no drawable elements - giving up unmasked this frame"); + return None; + } let mut target = match renderer.create_buffer(Fourcc::Argb8888, (w, h).into()) { Ok(t) => t, Err(e) => { |