backend: Support screen filters in nested mode

This commit is contained in:
Victoria Brekenfeld 2025-03-19 14:17:29 +01:00 committed by Victoria Brekenfeld
parent 63916410a0
commit 7373b3f513
4 changed files with 218 additions and 47 deletions

View file

@ -43,11 +43,16 @@ use smithay::{
damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult}, damage::{Error as RenderError, OutputDamageTracker, RenderOutputResult},
element::{ element::{
surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement}, surface::{render_elements_from_surface_tree, WaylandSurfaceRenderElement},
utils::{CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement}, texture::{TextureRenderBuffer, TextureRenderElement},
utils::{
constrain_render_elements, ConstrainAlign, ConstrainScaleBehavior,
CropRenderElement, Relocate, RelocateRenderElement, RescaleRenderElement,
},
AsRenderElements, Element, Id, Kind, RenderElement, AsRenderElements, Element, Id, Kind, RenderElement,
}, },
gles::{ gles::{
element::PixelShaderElement, GlesError, GlesPixelProgram, GlesRenderer, Uniform, element::{PixelShaderElement, TextureShaderElement},
GlesError, GlesPixelProgram, GlesRenderer, GlesTexProgram, GlesTexture, Uniform,
UniformName, UniformType, UniformName, UniformType,
}, },
glow::GlowRenderer, glow::GlowRenderer,
@ -1071,7 +1076,7 @@ pub struct ScreenFilterStorage {
} }
#[profiling::function] #[profiling::function]
pub fn render_output<'d, R, OffTarget>( pub fn render_output<'d, R>(
gpu: Option<&DrmNode>, gpu: Option<&DrmNode>,
renderer: &mut R, renderer: &mut R,
target: &mut R::Framebuffer<'_>, target: &mut R::Framebuffer<'_>,
@ -1081,6 +1086,7 @@ pub fn render_output<'d, R, OffTarget>(
now: Time<Monotonic>, now: Time<Monotonic>,
output: &Output, output: &Output,
cursor_mode: CursorMode, cursor_mode: CursorMode,
screen_filter: &'d mut ScreenFilterStorage,
) -> Result<RenderOutputResult<'d>, RenderError<R::Error>> ) -> Result<RenderOutputResult<'d>, RenderError<R::Error>>
where where
R: Renderer R: Renderer
@ -1088,7 +1094,7 @@ where
+ ImportMem + ImportMem
+ ExportMem + ExportMem
+ Bind<Dmabuf> + Bind<Dmabuf>
+ Offscreen<OffTarget> + Offscreen<GlesTexture>
+ Blit + Blit
+ AsGlowRenderer, + AsGlowRenderer,
R::TextureId: Send + Clone + 'static, R::TextureId: Send + Clone + 'static,
@ -1116,27 +1122,154 @@ where
ElementFilter::All ElementFilter::All
}; };
let result = render_workspace( let mut postprocess_texture = None;
gpu, let result = if !screen_filter.filter.is_noop() {
renderer, if screen_filter.state.as_ref().is_none_or(|state| {
target, state.output_config != PostprocessOutputConfig::for_output_untransformed(output)
damage_tracker, }) {
age, screen_filter.state = Some(
None, PostprocessState::new_with_renderer(
shell, renderer,
zoom_state.as_ref(), target.format().unwrap_or(Fourcc::Abgr8888),
now, PostprocessOutputConfig::for_output_untransformed(output),
output, )
previous_workspace, .map_err(RenderError::Rendering)?,
workspace, );
cursor_mode, }
element_filter,
); let state = screen_filter.state.as_mut().unwrap();
let mut result = Err(RenderError::OutputNoMode(OutputNoMode));
state
.texture
.render()
.draw::<_, RenderError<R::Error>>(|tex| {
let mut target = renderer.bind(tex).map_err(RenderError::Rendering)?;
result = render_workspace(
gpu,
renderer,
&mut target,
&mut state.damage_tracker,
1,
None,
shell,
zoom_state.as_ref(),
now,
output,
previous_workspace,
workspace,
cursor_mode,
element_filter,
);
std::mem::drop(target);
postprocess_texture = Some(tex.clone());
Ok(if let Ok((res, _)) = result.as_ref() {
renderer.wait(&res.sync).map_err(RenderError::Rendering)?;
let transform = output.current_transform();
let area = tex.size().to_logical(1, transform);
res.damage
.cloned()
.map(|v| {
v.into_iter()
.map(|r| r.to_logical(1).to_buffer(1, transform, &area))
.collect::<Vec<_>>()
})
.unwrap_or_default()
} else {
Vec::new()
})
})?;
if result.is_ok() {
let texture_elem = TextureRenderElement::from_texture_render_buffer(
(0., 0.),
&state.texture,
Some(1.0),
None,
None,
Kind::Unspecified,
);
let postprocess_texture_shader = renderer
.glow_renderer_mut()
.egl_context()
.user_data()
.get::<PostprocessShader>()
.expect("OffscreenShader should be available through `init_shaders`");
let texture_geometry =
texture_elem.geometry(output.current_scale().fractional_scale().into());
let elements = {
let texture_elem = TextureShaderElement::new(
texture_elem,
postprocess_texture_shader.0.clone(),
vec![
Uniform::new(
"invert",
if screen_filter.filter.inverted {
1.
} else {
0.
},
),
Uniform::new(
"color_mode",
screen_filter
.filter
.color_filter
.map(|val| val as u8 as f32)
.unwrap_or(0.),
),
],
);
constrain_render_elements(
std::iter::once(texture_elem),
(0, 0),
Rectangle::from_size(
output
.geometry()
.size
.as_logical()
.to_f64()
.to_physical(output.current_scale().fractional_scale())
.to_i32_round(),
),
texture_geometry,
ConstrainScaleBehavior::Fit,
ConstrainAlign::CENTER,
1.0,
)
.map(CosmicElement::Postprocess)
.collect::<Vec<_>>()
};
damage_tracker.render_output(renderer, target, age, &elements, CLEAR_COLOR)?;
}
result
} else {
render_workspace(
gpu,
renderer,
target,
damage_tracker,
age,
None,
shell,
zoom_state.as_ref(),
now,
output,
previous_workspace,
workspace,
cursor_mode,
element_filter,
)
};
match result { match result {
Ok((res, mut elements)) => { Ok((res, mut elements)) => {
for (session, frame) in output.take_pending_frames() { for (session, frame) in output.take_pending_frames() {
if let Some((frame, damage)) = render_session::<_, _, OffTarget>( if let Some((frame, damage)) = render_session::<_, _, GlesTexture>(
renderer, renderer,
&session.user_data().get::<SessionData>().unwrap(), &session.user_data().get::<SessionData>().unwrap(),
frame, frame,
@ -1184,24 +1317,46 @@ where
} }
if let (Some(ref damage), _) = &res { if let (Some(ref damage), _) = &res {
if let Ok(dmabuf) = get_dmabuf(buffer) { let blit_to_buffer =
let mut dmabuf_clone = dmabuf.clone(); |renderer: &mut R, blit_from: &mut R::Framebuffer<'_>| {
let mut fb = renderer if let Ok(dmabuf) = get_dmabuf(buffer) {
.bind(&mut dmabuf_clone) let mut dmabuf_clone = dmabuf.clone();
let mut fb = renderer.bind(&mut dmabuf_clone)?;
for rect in damage.iter() {
renderer.blit(
blit_from,
&mut fb,
*rect,
*rect,
TextureFilter::Nearest,
)?;
}
} else {
let fb = offscreen
.expect("shm buffers should have offscreen target");
for rect in damage.iter() {
renderer.blit(
blit_from,
fb,
*rect,
*rect,
TextureFilter::Nearest,
)?;
}
}
Result::<_, R::Error>::Ok(())
};
// we would want to just assign a different framebuffer to a variable, depending on the code-path,
// but then rustc tries to equate the lifetime of target with the lifetime of our temporary fb...
// So instead of duplicating all the code, we use a closure..
if let Some(tex) = postprocess_texture.as_mut() {
let mut fb = renderer.bind(tex).map_err(RenderError::Rendering)?;
blit_to_buffer(renderer, &mut fb)
.map_err(RenderError::Rendering)?; .map_err(RenderError::Rendering)?;
for rect in damage.iter() {
renderer
.blit(target, &mut fb, *rect, *rect, TextureFilter::Nearest)
.map_err(RenderError::Rendering)?;
}
} else { } else {
let fb = blit_to_buffer(renderer, target).map_err(RenderError::Rendering)?;
offscreen.expect("shm buffers should have offscreen target");
for rect in damage.iter() {
renderer
.blit(target, fb, *rect, *rect, TextureFilter::Nearest)
.map_err(RenderError::Rendering)?;
}
} }
} }

View file

@ -2,7 +2,7 @@
use crate::{ use crate::{
backend::render, backend::render,
config::OutputConfig, config::{OutputConfig, ScreenFilter},
shell::{Devices, SeatExt}, shell::{Devices, SeatExt},
state::{BackendData, Common}, state::{BackendData, Common},
utils::prelude::*, utils::prelude::*,
@ -14,7 +14,6 @@ use smithay::{
egl::EGLDevice, egl::EGLDevice,
renderer::{ renderer::{
damage::{OutputDamageTracker, RenderOutputResult}, damage::{OutputDamageTracker, RenderOutputResult},
gles::GlesRenderbuffer,
glow::GlowRenderer, glow::GlowRenderer,
ImportDma, ImportDma,
}, },
@ -34,7 +33,7 @@ use smithay::{
use std::{borrow::BorrowMut, cell::RefCell, time::Duration}; use std::{borrow::BorrowMut, cell::RefCell, time::Duration};
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use super::render::{init_shaders, CursorMode}; use super::render::{init_shaders, CursorMode, ScreenFilterStorage};
#[derive(Debug)] #[derive(Debug)]
pub struct WinitState { pub struct WinitState {
@ -42,6 +41,7 @@ pub struct WinitState {
pub backend: WinitGraphicsBackend<GlowRenderer>, pub backend: WinitGraphicsBackend<GlowRenderer>,
output: Output, output: Output,
damage_tracker: OutputDamageTracker, damage_tracker: OutputDamageTracker,
screen_filter_state: ScreenFilterStorage,
} }
impl WinitState { impl WinitState {
@ -52,7 +52,7 @@ impl WinitState {
.backend .backend
.bind() .bind()
.with_context(|| "Failed to bind buffer")?; .with_context(|| "Failed to bind buffer")?;
match render::render_output::<_, GlesRenderbuffer>( match render::render_output(
None, None,
renderer, renderer,
&mut fb, &mut fb,
@ -62,6 +62,7 @@ impl WinitState {
state.clock.now(), state.clock.now(),
&self.output, &self.output,
CursorMode::NotDefault, CursorMode::NotDefault,
&mut self.screen_filter_state,
) { ) {
Ok(RenderOutputResult { damage, states, .. }) => { Ok(RenderOutputResult { damage, states, .. }) => {
std::mem::drop(fb); std::mem::drop(fb);
@ -122,6 +123,11 @@ impl WinitState {
Ok(vec![self.output.clone()]) Ok(vec![self.output.clone()])
} }
} }
pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> Result<()> {
self.screen_filter_state.filter = screen_filter.clone();
Ok(())
}
} }
pub fn init_backend( pub fn init_backend(
@ -209,6 +215,7 @@ pub fn init_backend(
backend, backend,
output: output.clone(), output: output.clone(),
damage_tracker: OutputDamageTracker::from_output(&output), damage_tracker: OutputDamageTracker::from_output(&output),
screen_filter_state: ScreenFilterStorage::default(),
}); });
state state

View file

@ -2,7 +2,7 @@
use crate::{ use crate::{
backend::render, backend::render,
config::OutputConfig, config::{OutputConfig, ScreenFilter},
shell::{Devices, SeatExt}, shell::{Devices, SeatExt},
state::{BackendData, Common}, state::{BackendData, Common},
utils::prelude::*, utils::prelude::*,
@ -20,7 +20,6 @@ use smithay::{
input::{Event, InputEvent}, input::{Event, InputEvent},
renderer::{ renderer::{
damage::{OutputDamageTracker, RenderOutputResult}, damage::{OutputDamageTracker, RenderOutputResult},
gles::GlesRenderbuffer,
glow::GlowRenderer, glow::GlowRenderer,
Bind, ImportDma, Bind, ImportDma,
}, },
@ -41,7 +40,7 @@ use smithay::{
use std::{borrow::BorrowMut, cell::RefCell, os::unix::io::OwnedFd, time::Duration}; use std::{borrow::BorrowMut, cell::RefCell, os::unix::io::OwnedFd, time::Duration};
use tracing::{debug, error, info, warn}; use tracing::{debug, error, info, warn};
use super::render::init_shaders; use super::render::{init_shaders, ScreenFilterStorage};
#[derive(Debug)] #[derive(Debug)]
enum Allocator { enum Allocator {
@ -146,6 +145,7 @@ impl X11State {
render: ping.clone(), render: ping.clone(),
dirty: false, dirty: false,
pending: true, pending: true,
screen_filter_state: ScreenFilterStorage::default(),
}); });
// schedule first render // schedule first render
@ -187,6 +187,13 @@ impl X11State {
Ok(vec![surface.output.clone()]) Ok(vec![surface.output.clone()])
} }
} }
pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> Result<()> {
for surface in &mut self.surfaces {
surface.screen_filter_state.filter = screen_filter.clone();
}
Ok(())
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -198,6 +205,7 @@ pub struct Surface {
render: ping::Ping, render: ping::Ping,
dirty: bool, dirty: bool,
pending: bool, pending: bool,
screen_filter_state: ScreenFilterStorage,
} }
impl Surface { impl Surface {
@ -209,7 +217,7 @@ impl Surface {
let mut fb = renderer let mut fb = renderer
.bind(&mut buffer) .bind(&mut buffer)
.with_context(|| "Failed to bind dmabuf")?; .with_context(|| "Failed to bind dmabuf")?;
match render::render_output::<_, GlesRenderbuffer>( match render::render_output(
None, None,
renderer, renderer,
&mut fb, &mut fb,
@ -219,6 +227,7 @@ impl Surface {
state.clock.now(), state.clock.now(),
&self.output, &self.output,
render::CursorMode::NotDefault, render::CursorMode::NotDefault,
&mut self.screen_filter_state,
) { ) {
Ok(RenderOutputResult { damage, states, .. }) => { Ok(RenderOutputResult { damage, states, .. }) => {
self.surface self.surface

View file

@ -457,8 +457,8 @@ impl BackendData {
pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> anyhow::Result<()> { pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> anyhow::Result<()> {
match self { match self {
BackendData::Kms(ref mut state) => state.update_screen_filter(screen_filter), BackendData::Kms(ref mut state) => state.update_screen_filter(screen_filter),
BackendData::Winit(ref mut state) => {}, // TODO BackendData::Winit(ref mut state) => state.update_screen_filter(screen_filter),
BackendData::X11(ref mut state) => {}, // TODO BackendData::X11(ref mut state) => state.update_screen_filter(screen_filter),
_ => unreachable!("No backend set when setting screen filters"), _ => unreachable!("No backend set when setting screen filters"),
} }
} }