element/surface: Avoid scanning out translucent surfaces

This commit is contained in:
Victoria Brekenfeld 2026-07-14 13:17:06 +02:00 committed by Ashley Wulber
parent 7ee78d9c13
commit 603c90b103

View file

@ -4,7 +4,10 @@ use crate::{
wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree}, wayland::{SurfaceRenderElement, push_render_elements_from_surface_tree},
}, },
shell::focus::target::PointerFocusTarget, shell::focus::target::PointerFocusTarget,
wayland::handlers::{compositor::frame_time_filter_fn, corner_radius::surface_corners}, wayland::handlers::{
background_effect::ComputedBlurRegionCachedState, compositor::frame_time_filter_fn,
corner_radius::surface_corners,
},
}; };
use std::{ use std::{
borrow::Cow, borrow::Cow,
@ -19,7 +22,7 @@ use smithay::{
backend::{ backend::{
drm::DrmNode, drm::DrmNode,
renderer::{ renderer::{
ImportAll, Renderer, ImportAll, Renderer, buffer_has_alpha,
element::{Kind, RenderElementStates, surface::KindEvaluation}, element::{Kind, RenderElementStates, surface::KindEvaluation},
utils::RendererSurfaceStateUserData, utils::RendererSurfaceStateUserData,
}, },
@ -48,6 +51,7 @@ use smithay::{
IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap, IsAlive, Logical, Physical, Point, Rectangle, Scale, Serial, Size, user_data::UserDataMap,
}, },
wayland::{ wayland::{
alpha_modifier::AlphaModifierSurfaceCachedState,
compositor::{ compositor::{
SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states, SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states,
with_surface_tree_downward, with_surface_tree_downward,
@ -81,6 +85,39 @@ fn buffer_node(data: &SurfaceData) -> Option<DrmNode> {
.and_then(|dmabuf| dmabuf.node()) .and_then(|dmabuf| dmabuf.node())
} }
fn is_likely_translucent(alpha: f32, data: &SurfaceData) -> bool {
if alpha < 1.0 {
return true;
}
let mut alpha_modifier_state = data.cached_state.get::<AlphaModifierSurfaceCachedState>();
let alpha_multiplier = alpha_modifier_state
.current()
.multiplier_f32()
.unwrap_or(1.0);
if alpha_multiplier < 1.0 {
return true;
}
let Some(surface_state) = data.data_map.get::<RendererSurfaceStateUserData>() else {
return false;
};
let surface_state = surface_state.lock().unwrap();
if surface_state
.buffer()
.is_none_or(|buffer| !buffer_has_alpha(buffer).unwrap_or(true))
{
return false;
}
let mut blur_state = data.cached_state.get::<ComputedBlurRegionCachedState>();
blur_state
.current()
.blur_region
.as_ref()
.is_none_or(|region| region.is_empty())
}
/// Build the [`KindEvaluation`] for a window's surface tree. /// Build the [`KindEvaluation`] for a window's surface tree.
/// ///
/// `scanout_node`, when set, is the scan-out target [`DrmNode`] of the output currently being /// `scanout_node`, when set, is the scan-out target [`DrmNode`] of the output currently being
@ -89,6 +126,7 @@ fn buffer_node(data: &SurfaceData) -> Option<DrmNode> {
fn scanout_kind_eval( fn scanout_kind_eval(
scanout_override: Option<bool>, scanout_override: Option<bool>,
scanout_node: Option<DrmNode>, scanout_node: Option<DrmNode>,
alpha: f32,
) -> KindEvaluation { ) -> KindEvaluation {
match (scanout_override, scanout_node) { match (scanout_override, scanout_node) {
// Forced off. // Forced off.
@ -98,14 +136,14 @@ fn scanout_kind_eval(
(None, None) => FRAME_TIME_FILTER, (None, None) => FRAME_TIME_FILTER,
// Node restriction in effect: only buffers on the scan-out node may be candidates. // Node restriction in effect: only buffers on the scan-out node may be candidates.
(Some(true), Some(node)) => KindEvaluation::Closure(Box::new(move |data| { (Some(true), Some(node)) => KindEvaluation::Closure(Box::new(move |data| {
if buffer_node(data) == Some(node) { if buffer_node(data) == Some(node) && !is_likely_translucent(alpha, data) {
Kind::ScanoutCandidate Kind::ScanoutCandidate
} else { } else {
Kind::Unspecified Kind::Unspecified
} }
})), })),
(None, Some(node)) => KindEvaluation::Closure(Box::new(move |data| { (None, Some(node)) => KindEvaluation::Closure(Box::new(move |data| {
if buffer_node(data) == Some(node) { if buffer_node(data) == Some(node) && !is_likely_translucent(alpha, data) {
frame_time_filter_fn(data) frame_time_filter_fn(data)
} else { } else {
Kind::Unspecified Kind::Unspecified
@ -879,7 +917,7 @@ impl CosmicSurface {
radii, radii,
None, None,
blur_strength, blur_strength,
scanout_kind_eval(None, scanout_node), scanout_kind_eval(None, scanout_node, alpha),
push, push,
None, None,
) )
@ -924,7 +962,7 @@ impl CosmicSurface {
radii, radii,
None, None,
blur_strength, blur_strength,
scanout_kind_eval(scanout_override, scanout_node), scanout_kind_eval(scanout_override, scanout_node, alpha),
push_above, push_above,
push_below, push_below,
) )
@ -945,7 +983,7 @@ impl CosmicSurface {
radii, radii,
None, None,
blur_strength, blur_strength,
scanout_kind_eval(scanout_override, scanout_node), scanout_kind_eval(scanout_override, scanout_node, alpha),
push_above, push_above,
push_below, push_below,
) )