fix(kms): gate overlay scanout per-element by buffer GPU node
This commit is contained in:
parent
9d52653d5c
commit
012c77ec81
2 changed files with 95 additions and 42 deletions
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
init_shaders, output_elements,
|
init_shaders, output_elements,
|
||||||
},
|
},
|
||||||
config::ScreenFilter,
|
config::ScreenFilter,
|
||||||
shell::Shell,
|
shell::{Shell, element::surface::ScanoutTargetNodeGuard},
|
||||||
state::SurfaceDmabufFeedback,
|
state::SurfaceDmabufFeedback,
|
||||||
utils::prelude::*,
|
utils::prelude::*,
|
||||||
wayland::handlers::{
|
wayland::handlers::{
|
||||||
|
|
@ -1057,21 +1057,24 @@ impl SurfaceThreadState {
|
||||||
vrr = has_active_fullscreen;
|
vrr = has_active_fullscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut elements = output_elements(
|
let mut elements = {
|
||||||
Some(&render_node),
|
let _scanout_node = ScanoutTargetNodeGuard::new(Some(self.target_node));
|
||||||
&mut renderer,
|
output_elements(
|
||||||
&self.shell,
|
Some(&render_node),
|
||||||
self.clock.now(),
|
&mut renderer,
|
||||||
self.mirroring.as_ref().unwrap_or(&self.output),
|
&self.shell,
|
||||||
CursorMode::All,
|
self.clock.now(),
|
||||||
#[cfg(not(feature = "debug"))]
|
self.mirroring.as_ref().unwrap_or(&self.output),
|
||||||
None,
|
CursorMode::All,
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(not(feature = "debug"))]
|
||||||
Some((&self.egui, &self.timings)),
|
None,
|
||||||
)
|
#[cfg(feature = "debug")]
|
||||||
.map_err(|err| {
|
Some((&self.egui, &self.timings)),
|
||||||
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
|
)
|
||||||
})?;
|
.map_err(|err| {
|
||||||
|
anyhow::format_err!("Failed to accumulate elements for rendering: {:?}", err)
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
if vrr && fullscreen_drives_refresh_rate && !self.timings.past_min_render_time(&self.clock)
|
if vrr && fullscreen_drives_refresh_rate && !self.timings.past_min_render_time(&self.clock)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
cell::Cell,
|
||||||
sync::{
|
sync::{
|
||||||
Mutex,
|
Mutex,
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
|
|
@ -14,11 +15,17 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::renderer::{
|
backend::{
|
||||||
ImportAll, Renderer,
|
drm::DrmNode,
|
||||||
element::{
|
renderer::{
|
||||||
AsRenderElements, Kind, RenderElementStates,
|
ImportAll, Renderer,
|
||||||
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
|
element::{
|
||||||
|
AsRenderElements, Kind, RenderElementStates,
|
||||||
|
surface::{
|
||||||
|
KindEvaluation, WaylandSurfaceRenderElement, render_elements_from_surface_tree,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
utils::RendererSurfaceStateUserData,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
desktop::{
|
desktop::{
|
||||||
|
|
@ -49,6 +56,7 @@ use smithay::{
|
||||||
SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states,
|
SubsurfaceCachedState, SurfaceData, TraversalAction, get_parent, with_states,
|
||||||
with_surface_tree_downward,
|
with_surface_tree_downward,
|
||||||
},
|
},
|
||||||
|
dmabuf::get_dmabuf,
|
||||||
seat::WaylandFocus,
|
seat::WaylandFocus,
|
||||||
shell::xdg::{
|
shell::xdg::{
|
||||||
SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData,
|
SurfaceCachedState, ToplevelCachedState, ToplevelSurface, XdgToplevelSurfaceData,
|
||||||
|
|
@ -67,6 +75,66 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
/// The scan-out target [`DrmNode`] of the surface thread that is currently accumulating
|
||||||
|
/// render elements, if any.
|
||||||
|
static SCANOUT_TARGET_NODE: Cell<Option<DrmNode>> = const { Cell::new(None) };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RAII guard that sets the current thread's scan-out target node and restores the previous value
|
||||||
|
/// on drop. Hold it only across render-element accumulation so the gate can't outlive a render pass.
|
||||||
|
/// See [`SCANOUT_TARGET_NODE`].
|
||||||
|
pub struct ScanoutTargetNodeGuard(Option<DrmNode>);
|
||||||
|
|
||||||
|
impl ScanoutTargetNodeGuard {
|
||||||
|
pub fn new(node: Option<DrmNode>) -> Self {
|
||||||
|
ScanoutTargetNodeGuard(SCANOUT_TARGET_NODE.with(|n| n.replace(node)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for ScanoutTargetNodeGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
SCANOUT_TARGET_NODE.with(|n| n.set(self.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`DrmNode`] the surface's currently committed buffer was allocated on, if it is a dmabuf.
|
||||||
|
fn buffer_node(data: &SurfaceData) -> Option<DrmNode> {
|
||||||
|
let surface_state = data.data_map.get::<RendererSurfaceStateUserData>()?;
|
||||||
|
let surface_state = surface_state.lock().unwrap();
|
||||||
|
surface_state
|
||||||
|
.buffer()
|
||||||
|
.and_then(|buffer| get_dmabuf(buffer).ok())
|
||||||
|
.and_then(|dmabuf| dmabuf.node())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the [`KindEvaluation`] for a window's surface tree.
|
||||||
|
fn scanout_kind_eval(scanout_override: Option<bool>) -> KindEvaluation {
|
||||||
|
match (scanout_override, SCANOUT_TARGET_NODE.with(|n| n.get())) {
|
||||||
|
// Forced off.
|
||||||
|
(Some(false), _) => Kind::Unspecified.into(),
|
||||||
|
// No node restriction: preserve the previous behaviour exactly.
|
||||||
|
(Some(true), None) => Kind::ScanoutCandidate.into(),
|
||||||
|
(None, None) => FRAME_TIME_FILTER,
|
||||||
|
// Node restriction in effect: only buffers on the scan-out node may be candidates.
|
||||||
|
(Some(true), Some(node)) => KindEvaluation::Closure(Box::new(move |data| {
|
||||||
|
if buffer_node(data) == Some(node) {
|
||||||
|
Kind::ScanoutCandidate
|
||||||
|
} else {
|
||||||
|
Kind::Unspecified
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
(None, Some(node)) => KindEvaluation::Closure(Box::new(move |data| {
|
||||||
|
if buffer_node(data) == Some(node)
|
||||||
|
{
|
||||||
|
frame_time_filter_fn(data)
|
||||||
|
} else {
|
||||||
|
Kind::Unspecified
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
#[derive(Debug, Clone, PartialEq, Hash, Eq)]
|
||||||
pub struct CosmicSurface(pub Window);
|
pub struct CosmicSurface(pub Window);
|
||||||
|
|
||||||
|
|
@ -836,7 +904,7 @@ impl CosmicSurface {
|
||||||
location + offset,
|
location + offset,
|
||||||
scale,
|
scale,
|
||||||
alpha,
|
alpha,
|
||||||
FRAME_TIME_FILTER,
|
scanout_kind_eval(None),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
@ -868,16 +936,7 @@ impl CosmicSurface {
|
||||||
location,
|
location,
|
||||||
scale,
|
scale,
|
||||||
alpha,
|
alpha,
|
||||||
scanout_override
|
scanout_kind_eval(scanout_override),
|
||||||
.map(|val| {
|
|
||||||
if val {
|
|
||||||
Kind::ScanoutCandidate
|
|
||||||
} else {
|
|
||||||
Kind::Unspecified
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.unwrap_or(FRAME_TIME_FILTER),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
WindowSurface::X11(surface) => {
|
WindowSurface::X11(surface) => {
|
||||||
|
|
@ -891,16 +950,7 @@ impl CosmicSurface {
|
||||||
location,
|
location,
|
||||||
scale,
|
scale,
|
||||||
alpha,
|
alpha,
|
||||||
scanout_override
|
scanout_kind_eval(scanout_override),
|
||||||
.map(|val| {
|
|
||||||
if val {
|
|
||||||
Kind::ScanoutCandidate
|
|
||||||
} else {
|
|
||||||
Kind::Unspecified
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.unwrap_or(FRAME_TIME_FILTER),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue