kms: Support screen filters
This commit is contained in:
parent
18335c6758
commit
7929e25966
5 changed files with 198 additions and 80 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::{
|
||||
backend::render::{output_elements, CursorMode, GlMultiRenderer, CLEAR_COLOR},
|
||||
config::{AdaptiveSync, OutputConfig, OutputState},
|
||||
config::{AdaptiveSync, OutputConfig, OutputState, ScreenFilter},
|
||||
shell::Shell,
|
||||
utils::prelude::*,
|
||||
wayland::protocols::screencopy::Frame as ScreencopyFrame,
|
||||
|
|
@ -272,6 +272,7 @@ impl State {
|
|||
maybe_crtc,
|
||||
(w, 0),
|
||||
&self.common.event_loop_handle,
|
||||
self.common.config.dynamic_conf.screen_filter().clone(),
|
||||
self.common.shell.clone(),
|
||||
self.common.startup_done.clone(),
|
||||
) {
|
||||
|
|
@ -365,6 +366,7 @@ impl State {
|
|||
maybe_crtc,
|
||||
(w, 0),
|
||||
&self.common.event_loop_handle,
|
||||
self.common.config.dynamic_conf.screen_filter().clone(),
|
||||
self.common.shell.clone(),
|
||||
self.common.startup_done.clone(),
|
||||
) {
|
||||
|
|
@ -516,6 +518,7 @@ impl Device {
|
|||
maybe_crtc: Option<crtc::Handle>,
|
||||
position: (u32, u32),
|
||||
evlh: &LoopHandle<'static, State>,
|
||||
screen_filter: ScreenFilter,
|
||||
shell: Arc<RwLock<Shell>>,
|
||||
startup_done: Arc<AtomicBool>,
|
||||
) -> Result<(Output, bool)> {
|
||||
|
|
@ -579,6 +582,7 @@ impl Device {
|
|||
self.dev_node,
|
||||
self.render_node,
|
||||
evlh,
|
||||
screen_filter,
|
||||
shell,
|
||||
startup_done,
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use crate::{
|
||||
config::{AdaptiveSync, OutputState},
|
||||
config::{AdaptiveSync, OutputState, ScreenFilter},
|
||||
shell::Shell,
|
||||
state::BackendData,
|
||||
utils::prelude::*,
|
||||
|
|
@ -558,6 +558,7 @@ impl KmsState {
|
|||
&mut self,
|
||||
test_only: bool,
|
||||
loop_handle: &LoopHandle<'static, State>,
|
||||
screen_filter: &ScreenFilter,
|
||||
shell: Arc<RwLock<Shell>>,
|
||||
startup_done: Arc<AtomicBool>,
|
||||
clock: &Clock<Monotonic>,
|
||||
|
|
@ -658,6 +659,7 @@ impl KmsState {
|
|||
Some(crtc),
|
||||
(w, 0),
|
||||
loop_handle,
|
||||
screen_filter.clone(),
|
||||
shell.clone(),
|
||||
startup_done.clone(),
|
||||
)?;
|
||||
|
|
@ -927,4 +929,19 @@ impl KmsState {
|
|||
|
||||
Ok(all_outputs)
|
||||
}
|
||||
|
||||
pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> Result<()> {
|
||||
for device in self.drm_devices.values_mut() {
|
||||
for surface in device.surfaces.values_mut() {
|
||||
surface.set_screen_filter(screen_filter.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// We don't expect this to fail in a meaningful way.
|
||||
// The shader is already compiled at this point and we don't rely on any features,
|
||||
// that might not be available for any filters we currently expose.
|
||||
//
|
||||
// But we might conditionally fail here in the future.
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
init_shaders, output_elements, CursorMode, GlMultiRenderer, PostprocessOutputConfig,
|
||||
PostprocessShader, PostprocessState, CLEAR_COLOR,
|
||||
},
|
||||
config::AdaptiveSync,
|
||||
config::{AdaptiveSync, ScreenFilter},
|
||||
shell::Shell,
|
||||
state::SurfaceDmabufFeedback,
|
||||
utils::prelude::*,
|
||||
|
|
@ -40,12 +40,12 @@ use smithay::{
|
|||
utils::{constrain_render_elements, ConstrainAlign, ConstrainScaleBehavior},
|
||||
Element, Kind, RenderElementStates,
|
||||
},
|
||||
gles::{GlesRenderbuffer, GlesTexture},
|
||||
gles::{element::TextureShaderElement, GlesRenderbuffer, GlesRenderer, Uniform},
|
||||
glow::GlowRenderer,
|
||||
multigpu::{Error as MultiError, GpuManager},
|
||||
sync::SyncPoint,
|
||||
utils::with_renderer_surface_state,
|
||||
Bind, ImportDma, Offscreen, Renderer, RendererSuper, Texture,
|
||||
Bind, Blit, ImportDma, Offscreen, Renderer, RendererSuper, Texture, TextureFilter,
|
||||
},
|
||||
},
|
||||
desktop::utils::OutputPresentationFeedback,
|
||||
|
|
@ -74,7 +74,7 @@ use smithay::{
|
|||
use tracing::{error, trace, warn};
|
||||
|
||||
use std::{
|
||||
borrow::BorrowMut,
|
||||
borrow::{Borrow, BorrowMut},
|
||||
collections::{hash_map, HashMap, HashSet},
|
||||
mem,
|
||||
sync::{
|
||||
|
|
@ -129,6 +129,7 @@ pub struct SurfaceThreadState {
|
|||
|
||||
output: Output,
|
||||
mirroring: Option<Output>,
|
||||
screen_filter: ScreenFilter,
|
||||
postprocess_textures: HashMap<DrmNode, PostprocessState>,
|
||||
|
||||
shell: Arc<RwLock<Shell>>,
|
||||
|
|
@ -190,6 +191,7 @@ pub enum ThreadCommand {
|
|||
node: DrmNode,
|
||||
},
|
||||
UpdateMirroring(Option<Output>),
|
||||
UpdateScreenFilter(ScreenFilter),
|
||||
VBlank(Option<DrmEventMetadata>),
|
||||
ScheduleRender,
|
||||
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
|
||||
|
|
@ -214,6 +216,7 @@ impl Surface {
|
|||
dev_node: DrmNode,
|
||||
target_node: DrmNode,
|
||||
evlh: &LoopHandle<'static, State>,
|
||||
screen_filter: ScreenFilter,
|
||||
shell: Arc<RwLock<Shell>>,
|
||||
startup_done: Arc<AtomicBool>,
|
||||
) -> Result<Self> {
|
||||
|
|
@ -233,6 +236,7 @@ impl Surface {
|
|||
target_node,
|
||||
shell,
|
||||
active_clone,
|
||||
screen_filter,
|
||||
tx2,
|
||||
rx,
|
||||
startup_done,
|
||||
|
|
@ -354,6 +358,12 @@ impl Surface {
|
|||
.send(ThreadCommand::UpdateMirroring(output));
|
||||
}
|
||||
|
||||
pub fn set_screen_filter(&mut self, config: ScreenFilter) {
|
||||
let _ = self
|
||||
.thread_command
|
||||
.send(ThreadCommand::UpdateScreenFilter(config));
|
||||
}
|
||||
|
||||
pub fn adaptive_sync_support(&self) -> Result<VrrSupport> {
|
||||
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||
let _ = self
|
||||
|
|
@ -447,6 +457,7 @@ fn surface_thread(
|
|||
target_node: DrmNode,
|
||||
shell: Arc<RwLock<Shell>>,
|
||||
active: Arc<AtomicBool>,
|
||||
screen_filter: ScreenFilter,
|
||||
thread_sender: Sender<SurfaceCommand>,
|
||||
thread_receiver: Channel<ThreadCommand>,
|
||||
startup_done: Arc<AtomicBool>,
|
||||
|
|
@ -490,6 +501,7 @@ fn surface_thread(
|
|||
|
||||
output,
|
||||
mirroring: None,
|
||||
screen_filter,
|
||||
postprocess_textures: HashMap::new(),
|
||||
|
||||
shell,
|
||||
|
|
@ -528,6 +540,9 @@ fn surface_thread(
|
|||
Event::Msg(ThreadCommand::UpdateMirroring(mirroring_output)) => {
|
||||
state.update_mirroring(mirroring_output);
|
||||
}
|
||||
Event::Msg(ThreadCommand::UpdateScreenFilter(filter_config)) => {
|
||||
state.update_screen_filter(filter_config);
|
||||
}
|
||||
Event::Msg(ThreadCommand::AdaptiveSyncAvailable(result)) => {
|
||||
if let Some(compositor) = state.compositor.as_mut() {
|
||||
let _ = result.send(
|
||||
|
|
@ -989,11 +1004,15 @@ impl SurfaceThreadState {
|
|||
let source_output = self
|
||||
.mirroring
|
||||
.as_ref()
|
||||
.or((!self.screen_filter.is_noop()).then(|| &self.output))
|
||||
.filter(|output| {
|
||||
PostprocessOutputConfig::for_output_untransformed(output)
|
||||
!= PostprocessOutputConfig::for_output(&self.output)
|
||||
|| !self.screen_filter.is_noop()
|
||||
});
|
||||
|
||||
let mut pre_postprocess_states = None;
|
||||
let mut pre_postprocess_texture = None;
|
||||
let res = if let Some(source_output) = source_output {
|
||||
let offscreen_output_config =
|
||||
PostprocessOutputConfig::for_output_untransformed(source_output);
|
||||
|
|
@ -1023,6 +1042,10 @@ impl SurfaceThreadState {
|
|||
.texture
|
||||
.render()
|
||||
.draw::<_, <GlMultiRenderer as RendererSuper>::Error>(|tex| {
|
||||
if self.mirroring.is_none() {
|
||||
pre_postprocess_texture = Some(tex.clone());
|
||||
}
|
||||
|
||||
let mut fb = renderer.bind(tex)?;
|
||||
let res = match postprocess_state.damage_tracker.render_output(
|
||||
&mut renderer,
|
||||
|
|
@ -1035,6 +1058,11 @@ impl SurfaceThreadState {
|
|||
Err(RenderError::Rendering(err)) => return Err(err),
|
||||
Err(RenderError::OutputNoMode(_)) => unreachable!(),
|
||||
};
|
||||
|
||||
if self.mirroring.is_none() {
|
||||
pre_postprocess_states = Some(res.states);
|
||||
}
|
||||
|
||||
renderer.wait(&res.sync)?;
|
||||
std::mem::drop(fb);
|
||||
|
||||
|
|
@ -1061,29 +1089,52 @@ impl SurfaceThreadState {
|
|||
None,
|
||||
Kind::Unspecified,
|
||||
);
|
||||
let texture_geometry =
|
||||
texture_elem.geometry(self.output.current_scale().fractional_scale().into());
|
||||
elements = constrain_render_elements(
|
||||
std::iter::once(texture_elem),
|
||||
(0, 0),
|
||||
Rectangle::from_size(
|
||||
self.output
|
||||
.geometry()
|
||||
.size
|
||||
.as_logical()
|
||||
.to_f64()
|
||||
.to_physical(self.output.current_scale().fractional_scale())
|
||||
.to_i32_round(),
|
||||
),
|
||||
texture_geometry,
|
||||
ConstrainScaleBehavior::Fit,
|
||||
ConstrainAlign::CENTER,
|
||||
1.0,
|
||||
)
|
||||
.map(CosmicElement::Postprocess)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
renderer = self.api.single_renderer(&self.target_node).unwrap();
|
||||
|
||||
let postprocess_texture_shader = Borrow::<GlesRenderer>::borrow(renderer.as_mut())
|
||||
.egl_context()
|
||||
.user_data()
|
||||
.get::<PostprocessShader>()
|
||||
.expect("OffscreenShader should be available through `init_shaders`");
|
||||
let texture_geometry =
|
||||
texture_elem.geometry(self.output.current_scale().fractional_scale().into());
|
||||
elements = {
|
||||
let texture_elem = TextureShaderElement::new(
|
||||
texture_elem,
|
||||
postprocess_texture_shader.0.clone(),
|
||||
vec![
|
||||
Uniform::new("invert", if self.screen_filter.inverted { 1. } else { 0. }),
|
||||
Uniform::new(
|
||||
"color_mode",
|
||||
self.screen_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(
|
||||
self.output
|
||||
.geometry()
|
||||
.size
|
||||
.as_logical()
|
||||
.to_f64()
|
||||
.to_physical(self.output.current_scale().fractional_scale())
|
||||
.to_i32_round(),
|
||||
),
|
||||
texture_geometry,
|
||||
ConstrainScaleBehavior::Fit,
|
||||
ConstrainAlign::CENTER,
|
||||
1.0,
|
||||
)
|
||||
.map(CosmicElement::Postprocess)
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
if let Err(err) = compositor.with_compositor(|c| c.use_vrr(vrr)) {
|
||||
warn!("Unable to set adaptive VRR state: {}", err);
|
||||
}
|
||||
|
|
@ -1150,16 +1201,15 @@ impl SurfaceThreadState {
|
|||
};
|
||||
|
||||
let mut sync = SyncPoint::default();
|
||||
|
||||
let mut dmabuf_clone;
|
||||
let mut render_buffer;
|
||||
let buffer = frame.buffer();
|
||||
let mut shm_buffer = false;
|
||||
let mut fb = if let Ok(dmabuf) = get_dmabuf(&buffer) {
|
||||
dmabuf_clone = dmabuf.clone();
|
||||
renderer
|
||||
Some(renderer
|
||||
.bind(&mut dmabuf_clone)
|
||||
.map_err(RenderError::<<GlMultiRenderer as RendererSuper>::Error>::Rendering)?
|
||||
.map_err(RenderError::<<GlMultiRenderer as RendererSuper>::Error>::Rendering)?)
|
||||
} else {
|
||||
shm_buffer = true;
|
||||
let size = buffer_dimensions(&buffer).ok_or(RenderError::<
|
||||
|
|
@ -1171,16 +1221,24 @@ impl SurfaceThreadState {
|
|||
with_buffer_contents(&buffer, |_, _, data| shm_format_to_fourcc(data.format))
|
||||
.map_err(|_| OutputNoMode)? // eh, we have to do some error
|
||||
.expect("We should be able to convert all hardcoded shm screencopy formats");
|
||||
render_buffer =
|
||||
Offscreen::<GlesRenderbuffer>::create_buffer(
|
||||
&mut renderer,
|
||||
format,
|
||||
size,
|
||||
)
|
||||
.map_err(RenderError::<<GlMultiRenderer as RendererSuper>::Error>::Rendering)?;
|
||||
renderer
|
||||
.bind(&mut render_buffer)
|
||||
.map_err(RenderError::<<GlMultiRenderer as RendererSuper>::Error>::Rendering)?
|
||||
|
||||
if pre_postprocess_texture
|
||||
.as_ref()
|
||||
.is_some_and(|tex| tex.format() == Some(format))
|
||||
{
|
||||
None
|
||||
} else {
|
||||
render_buffer =
|
||||
Offscreen::<GlesRenderbuffer>::create_buffer(
|
||||
&mut renderer,
|
||||
format,
|
||||
size,
|
||||
)
|
||||
.map_err(RenderError::<<GlMultiRenderer as RendererSuper>::Error>::Rendering)?;
|
||||
Some(renderer
|
||||
.bind(&mut render_buffer)
|
||||
.map_err(RenderError::<<GlMultiRenderer as RendererSuper>::Error>::Rendering)?)
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(ref damage) = damage {
|
||||
|
|
@ -1214,52 +1272,76 @@ impl SurfaceThreadState {
|
|||
d
|
||||
});
|
||||
|
||||
match frame_result
|
||||
.blit_frame_result(
|
||||
output_size,
|
||||
output_transform,
|
||||
output_scale,
|
||||
&mut renderer,
|
||||
&mut fb,
|
||||
adjusted,
|
||||
filter,
|
||||
)
|
||||
.map_err(|err| match err {
|
||||
BlitFrameResultError::Rendering(err) => RenderError::<
|
||||
<GlMultiRenderer as RendererSuper>::Error,
|
||||
>::Rendering(
|
||||
err
|
||||
),
|
||||
BlitFrameResultError::Export(_) => RenderError::<
|
||||
<GlMultiRenderer as RendererSuper>::Error,
|
||||
>::Rendering(
|
||||
MultiError::DeviceMissing,
|
||||
),
|
||||
}) {
|
||||
Ok(new_sync) => {
|
||||
sync = new_sync;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to screencopy");
|
||||
session
|
||||
.user_data()
|
||||
.get::<SessionData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.reset();
|
||||
frame.fail(FailureReason::Unknown);
|
||||
continue;
|
||||
if let Some(tex) = pre_postprocess_texture.as_mut() {
|
||||
let mut tex_fb = renderer.bind(tex).map_err(RenderError::<<GlMultiRenderer as RendererSuper>::Error>::Rendering)?;
|
||||
|
||||
if let Some(fb) = fb.as_mut() {
|
||||
for rect in adjusted {
|
||||
renderer
|
||||
.blit(
|
||||
&mut tex_fb,
|
||||
fb,
|
||||
rect,
|
||||
rect,
|
||||
TextureFilter::Linear,
|
||||
)
|
||||
.map_err(
|
||||
RenderError::<
|
||||
<GlMultiRenderer as RendererSuper>::Error,
|
||||
>::Rendering,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
fb = Some(tex_fb);
|
||||
}
|
||||
} else {
|
||||
match frame_result
|
||||
.blit_frame_result(
|
||||
output_size,
|
||||
output_transform,
|
||||
output_scale,
|
||||
&mut renderer,
|
||||
fb.as_mut().unwrap(),
|
||||
adjusted,
|
||||
filter,
|
||||
)
|
||||
.map_err(|err| match err {
|
||||
BlitFrameResultError::Rendering(err) => RenderError::<
|
||||
<GlMultiRenderer as RendererSuper>::Error,
|
||||
>::Rendering(
|
||||
err
|
||||
),
|
||||
BlitFrameResultError::Export(_) => RenderError::<
|
||||
<GlMultiRenderer as RendererSuper>::Error,
|
||||
>::Rendering(
|
||||
MultiError::DeviceMissing,
|
||||
),
|
||||
}) {
|
||||
Ok(new_sync) => {
|
||||
sync = new_sync;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::warn!(?err, "Failed to screencopy");
|
||||
session
|
||||
.user_data()
|
||||
.get::<SessionData>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.reset();
|
||||
frame.fail(FailureReason::Unknown);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
let transform = self.output.current_transform();
|
||||
|
||||
match submit_buffer(
|
||||
frame,
|
||||
&mut renderer,
|
||||
shm_buffer.then_some(&mut fb),
|
||||
shm_buffer.then_some(fb.as_mut().unwrap()),
|
||||
transform,
|
||||
damage.as_deref(),
|
||||
sync,
|
||||
|
|
@ -1286,7 +1368,8 @@ impl SurfaceThreadState {
|
|||
}
|
||||
|
||||
if self.mirroring.is_none() {
|
||||
let states = frame_result.states;
|
||||
// If postprocessing, use states from first render
|
||||
let states = pre_postprocess_states.unwrap_or(frame_result.states);
|
||||
self.send_dmabuf_feedback(states);
|
||||
}
|
||||
|
||||
|
|
@ -1386,6 +1469,11 @@ impl SurfaceThreadState {
|
|||
self.postprocess_textures.clear();
|
||||
}
|
||||
|
||||
fn update_screen_filter(&mut self, filter_config: ScreenFilter) {
|
||||
self.screen_filter = filter_config;
|
||||
self.postprocess_textures.clear();
|
||||
}
|
||||
|
||||
fn send_frame_callbacks(&mut self) {
|
||||
if self.mirroring.is_none() {
|
||||
let _ = self
|
||||
|
|
|
|||
|
|
@ -308,6 +308,7 @@ impl BackendData {
|
|||
&mut self,
|
||||
test_only: bool,
|
||||
loop_handle: &LoopHandle<'static, State>,
|
||||
screen_filter: &ScreenFilter,
|
||||
shell: Arc<RwLock<Shell>>,
|
||||
workspace_state: &mut WorkspaceUpdateGuard<'_, State>,
|
||||
xdg_activation_state: &XdgActivationState,
|
||||
|
|
@ -318,6 +319,7 @@ impl BackendData {
|
|||
BackendData::Kms(ref mut state) => state.apply_config_for_outputs(
|
||||
test_only,
|
||||
loop_handle,
|
||||
screen_filter,
|
||||
shell.clone(),
|
||||
startup_done,
|
||||
clock,
|
||||
|
|
@ -453,7 +455,12 @@ impl BackendData {
|
|||
}
|
||||
|
||||
pub fn update_screen_filter(&mut self, screen_filter: &ScreenFilter) -> anyhow::Result<()> {
|
||||
let _ = screen_filter; // TODO
|
||||
match self {
|
||||
BackendData::Kms(ref mut state) => state.update_screen_filter(screen_filter),
|
||||
BackendData::Winit(ref mut state) => {}, // TODO
|
||||
BackendData::X11(ref mut state) => {}, // TODO
|
||||
_ => unreachable!("No backend set when setting screen filters"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ impl State {
|
|||
let res = self.backend.apply_config_for_outputs(
|
||||
test_only,
|
||||
&self.common.event_loop_handle,
|
||||
self.common.config.dynamic_conf.screen_filter(),
|
||||
self.common.shell.clone(),
|
||||
&mut self.common.workspace_state.update(),
|
||||
&self.common.xdg_activation_state,
|
||||
|
|
@ -158,6 +159,7 @@ impl State {
|
|||
if let Err(err) = self.backend.apply_config_for_outputs(
|
||||
false,
|
||||
&self.common.event_loop_handle,
|
||||
self.common.config.dynamic_conf.screen_filter(),
|
||||
self.common.shell.clone(),
|
||||
&mut self.common.workspace_state.update(),
|
||||
&self.common.xdg_activation_state,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue