kms: Allow diverging primary plane formats under certain conditions
This commit is contained in:
parent
b5cd62fd7a
commit
6be5009b37
5 changed files with 169 additions and 43 deletions
|
|
@ -13,8 +13,9 @@ use libc::dev_t;
|
||||||
use smithay::{
|
use smithay::{
|
||||||
backend::{
|
backend::{
|
||||||
allocator::{
|
allocator::{
|
||||||
|
format::FormatSet,
|
||||||
gbm::{GbmAllocator, GbmDevice},
|
gbm::{GbmAllocator, GbmDevice},
|
||||||
Fourcc,
|
Format, Fourcc,
|
||||||
},
|
},
|
||||||
drm::{
|
drm::{
|
||||||
compositor::FrameFlags, output::DrmOutputManager, DrmDevice, DrmDeviceFd, DrmEvent,
|
compositor::FrameFlags, output::DrmOutputManager, DrmDevice, DrmDeviceFd, DrmEvent,
|
||||||
|
|
@ -612,25 +613,31 @@ impl Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allow_overlay_scanout(
|
fn allow_frame_flags(
|
||||||
&mut self,
|
&mut self,
|
||||||
flag: bool,
|
flag: bool,
|
||||||
|
flags: FrameFlags,
|
||||||
renderer: &mut GlMultiRenderer,
|
renderer: &mut GlMultiRenderer,
|
||||||
clock: &Clock<Monotonic>,
|
clock: &Clock<Monotonic>,
|
||||||
shell: &Arc<RwLock<Shell>>,
|
shell: &Arc<RwLock<Shell>>,
|
||||||
|
startup_done: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
for surface in self.surfaces.values_mut() {
|
for surface in self.surfaces.values_mut() {
|
||||||
surface.allow_overlay_scanout(flag);
|
surface.allow_frame_flags(flag, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !flag {
|
if !flag {
|
||||||
let now = clock.now();
|
let now = clock.now();
|
||||||
|
|
||||||
let output_map = self
|
let mut output_map = self
|
||||||
.surfaces
|
.surfaces
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|(_, s)| s.is_active())
|
||||||
.map(|(crtc, surface)| (*crtc, surface.output.clone()))
|
.map(|(crtc, surface)| (*crtc, surface.output.clone()))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
if !startup_done {
|
||||||
|
output_map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
self.drm.with_compositors::<Result<()>>(|map| {
|
self.drm.with_compositors::<Result<()>>(|map| {
|
||||||
for (crtc, compositor) in map.iter() {
|
for (crtc, compositor) in map.iter() {
|
||||||
|
|
@ -665,6 +672,65 @@ impl Device {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn allow_overlay_scanout(
|
||||||
|
&mut self,
|
||||||
|
flag: bool,
|
||||||
|
renderer: &mut GlMultiRenderer,
|
||||||
|
clock: &Clock<Monotonic>,
|
||||||
|
shell: &Arc<RwLock<Shell>>,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.allow_frame_flags(
|
||||||
|
flag,
|
||||||
|
FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT,
|
||||||
|
renderer,
|
||||||
|
clock,
|
||||||
|
shell,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn allow_primary_scanout_any(
|
||||||
|
&mut self,
|
||||||
|
flag: bool,
|
||||||
|
renderer: &mut GlMultiRenderer,
|
||||||
|
clock: &Clock<Monotonic>,
|
||||||
|
shell: &Arc<RwLock<Shell>>,
|
||||||
|
startup_done: bool,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.allow_frame_flags(
|
||||||
|
flag,
|
||||||
|
FrameFlags::ALLOW_PRIMARY_PLANE_SCANOUT_ANY,
|
||||||
|
renderer,
|
||||||
|
clock,
|
||||||
|
shell,
|
||||||
|
startup_done,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.drm.with_compositors(|comps| {
|
||||||
|
for (crtc, comp) in comps {
|
||||||
|
let Some(surface) = self.surfaces.get_mut(crtc) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
let comp = comp.lock().unwrap();
|
||||||
|
surface.primary_plane_formats = if flag {
|
||||||
|
comp.surface().plane_info().formats.clone()
|
||||||
|
} else {
|
||||||
|
// This certainly isn't perfect and might still miss the happy path,
|
||||||
|
// but it is surprisingly difficult to hack an api into smithay,
|
||||||
|
// to get the actual framebuffer format
|
||||||
|
let code = comp.format();
|
||||||
|
FormatSet::from_iter(comp.modifiers().iter().map(|mo| Format {
|
||||||
|
code,
|
||||||
|
modifier: *mo,
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
surface.feedback.clear();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn in_use(&self, primary: Option<&DrmNode>) -> bool {
|
pub fn in_use(&self, primary: Option<&DrmNode>) -> bool {
|
||||||
Some(&self.render_node) == primary
|
Some(&self.render_node) == primary
|
||||||
|| !self.surfaces.is_empty()
|
|| !self.surfaces.is_empty()
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,10 @@ use std::{
|
||||||
borrow::BorrowMut,
|
borrow::BorrowMut,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
path::Path,
|
path::Path,
|
||||||
sync::{atomic::AtomicBool, Arc, RwLock},
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc, RwLock,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod device;
|
mod device;
|
||||||
|
|
@ -646,16 +649,44 @@ impl KmsState {
|
||||||
.context("Failed to enable devices")?;
|
.context("Failed to enable devices")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let startup_done = startup_done.load(Ordering::SeqCst);
|
||||||
let mut all_outputs = Vec::new();
|
let mut all_outputs = Vec::new();
|
||||||
for device in self.drm_devices.values_mut() {
|
for device in self.drm_devices.values_mut() {
|
||||||
// reconfigure existing
|
|
||||||
let now = clock.now();
|
let now = clock.now();
|
||||||
let output_map = device
|
let mut output_map = device
|
||||||
.surfaces
|
.surfaces
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|(_, s)| s.is_active())
|
||||||
.map(|(crtc, surface)| (*crtc, surface.output.clone()))
|
.map(|(crtc, surface)| (*crtc, surface.output.clone()))
|
||||||
.collect::<HashMap<_, _>>();
|
.collect::<HashMap<_, _>>();
|
||||||
|
if !startup_done {
|
||||||
|
output_map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// configure primary scanout allowance
|
||||||
|
if !device.surfaces.is_empty() {
|
||||||
|
let mut renderer = self
|
||||||
|
.api
|
||||||
|
.single_renderer(&device.render_node)
|
||||||
|
.with_context(|| "Failed to create renderer")?;
|
||||||
|
|
||||||
|
device
|
||||||
|
.allow_primary_scanout_any(
|
||||||
|
device
|
||||||
|
.surfaces
|
||||||
|
.values()
|
||||||
|
.filter(|s| s.output.is_enabled())
|
||||||
|
.count()
|
||||||
|
<= 1,
|
||||||
|
&mut renderer,
|
||||||
|
clock,
|
||||||
|
&shell,
|
||||||
|
startup_done,
|
||||||
|
)
|
||||||
|
.context("Failed to switch primary-plane scanout flags")?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reconfigure existing
|
||||||
for (crtc, surface) in device.surfaces.iter_mut() {
|
for (crtc, surface) in device.surfaces.iter_mut() {
|
||||||
let output_config = surface.output.config();
|
let output_config = surface.output.config();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,9 @@ pub struct Surface {
|
||||||
known_nodes: HashSet<DrmNode>,
|
known_nodes: HashSet<DrmNode>,
|
||||||
|
|
||||||
active: Arc<AtomicBool>,
|
active: Arc<AtomicBool>,
|
||||||
feedback: HashMap<DrmNode, SurfaceDmabufFeedback>,
|
pub(super) feedback: HashMap<DrmNode, SurfaceDmabufFeedback>,
|
||||||
plane_formats: FormatSet,
|
pub(super) primary_plane_formats: FormatSet,
|
||||||
|
overlay_plane_formats: FormatSet,
|
||||||
|
|
||||||
loop_handle: LoopHandle<'static, State>,
|
loop_handle: LoopHandle<'static, State>,
|
||||||
thread_command: Sender<ThreadCommand>,
|
thread_command: Sender<ThreadCommand>,
|
||||||
|
|
@ -239,7 +240,7 @@ pub enum ThreadCommand {
|
||||||
ScheduleRender,
|
ScheduleRender,
|
||||||
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
|
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
|
||||||
UseAdaptiveSync(AdaptiveSync),
|
UseAdaptiveSync(AdaptiveSync),
|
||||||
AllowOverlayScanout(bool, SyncSender<()>),
|
AllowFrameFlags(bool, FrameFlags, SyncSender<()>),
|
||||||
End,
|
End,
|
||||||
DpmsOff,
|
DpmsOff,
|
||||||
}
|
}
|
||||||
|
|
@ -309,6 +310,7 @@ impl Surface {
|
||||||
.surfaces
|
.surfaces
|
||||||
.get_mut(&crtc)
|
.get_mut(&crtc)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
state
|
state
|
||||||
.common
|
.common
|
||||||
.send_dmabuf_feedback(&output_clone, &states, |source_node| {
|
.send_dmabuf_feedback(&output_clone, &states, |source_node| {
|
||||||
|
|
@ -332,7 +334,8 @@ impl Surface {
|
||||||
target_node,
|
target_node,
|
||||||
render_formats,
|
render_formats,
|
||||||
target_formats,
|
target_formats,
|
||||||
surface.plane_formats.clone(),
|
surface.primary_plane_formats.clone(),
|
||||||
|
surface.overlay_plane_formats.clone(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.clone(),
|
.clone(),
|
||||||
|
|
@ -350,7 +353,8 @@ impl Surface {
|
||||||
known_nodes: HashSet::new(),
|
known_nodes: HashSet::new(),
|
||||||
active,
|
active,
|
||||||
feedback: HashMap::new(),
|
feedback: HashMap::new(),
|
||||||
plane_formats: FormatSet::default(),
|
primary_plane_formats: FormatSet::default(),
|
||||||
|
overlay_plane_formats: FormatSet::default(),
|
||||||
loop_handle: evlh.clone(),
|
loop_handle: evlh.clone(),
|
||||||
thread_command: tx,
|
thread_command: tx,
|
||||||
thread_token,
|
thread_token,
|
||||||
|
|
@ -423,11 +427,11 @@ impl Surface {
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allow_overlay_scanout(&mut self, flag: bool) {
|
pub fn allow_frame_flags(&mut self, flag: bool, flags: FrameFlags) {
|
||||||
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||||
let _ = self
|
let _ = self
|
||||||
.thread_command
|
.thread_command
|
||||||
.send(ThreadCommand::AllowOverlayScanout(flag, tx));
|
.send(ThreadCommand::AllowFrameFlags(flag, flags, tx));
|
||||||
let _ = rx.recv();
|
let _ = rx.recv();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -439,21 +443,18 @@ impl Surface {
|
||||||
|
|
||||||
pub fn resume(&mut self, compositor: GbmDrmOutput) -> Result<()> {
|
pub fn resume(&mut self, compositor: GbmDrmOutput) -> Result<()> {
|
||||||
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||||
self.plane_formats = compositor.with_compositor(|c| {
|
(self.primary_plane_formats, self.overlay_plane_formats) =
|
||||||
c.surface()
|
compositor.with_compositor(|c| {
|
||||||
.plane_info()
|
(
|
||||||
.formats
|
c.surface().plane_info().formats.clone(),
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.chain(
|
|
||||||
c.surface()
|
c.surface()
|
||||||
.planes()
|
.planes()
|
||||||
.overlay
|
.overlay
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|p| p.formats.iter().cloned()),
|
.flat_map(|p| p.formats.iter().cloned())
|
||||||
|
.collect::<FormatSet>(),
|
||||||
)
|
)
|
||||||
.collect::<FormatSet>()
|
});
|
||||||
});
|
|
||||||
|
|
||||||
let _ = self.thread_command.send(ThreadCommand::Resume {
|
let _ = self.thread_command.send(ThreadCommand::Resume {
|
||||||
compositor,
|
compositor,
|
||||||
|
|
@ -615,20 +616,18 @@ fn surface_thread(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Msg(ThreadCommand::AllowOverlayScanout(flag, tx)) => {
|
Event::Msg(ThreadCommand::AllowFrameFlags(flag, mut flags, tx)) => {
|
||||||
if !crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT").unwrap_or(false)
|
if crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT").unwrap_or(false) {
|
||||||
&& !crate::utils::env::bool_var("COSMIC_DISABLE_OVERLAY_SCANOUT")
|
flags.remove(FrameFlags::ALLOW_SCANOUT);
|
||||||
.unwrap_or(false)
|
}
|
||||||
{
|
if crate::utils::env::bool_var("COSMIC_DISABLE_OVERLAY_SCANOUT").unwrap_or(false) {
|
||||||
if flag {
|
flags.remove(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);
|
||||||
state
|
}
|
||||||
.frame_flags
|
|
||||||
.insert(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);
|
if flag {
|
||||||
} else {
|
state.frame_flags.insert(flags);
|
||||||
state
|
} else {
|
||||||
.frame_flags
|
state.frame_flags.remove(flags);
|
||||||
.remove(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let _ = tx.send(());
|
let _ = tx.send(());
|
||||||
}
|
}
|
||||||
|
|
@ -1484,7 +1483,8 @@ fn get_surface_dmabuf_feedback(
|
||||||
target_node: DrmNode,
|
target_node: DrmNode,
|
||||||
render_formats: FormatSet,
|
render_formats: FormatSet,
|
||||||
target_formats: FormatSet,
|
target_formats: FormatSet,
|
||||||
plane_formats: FormatSet,
|
primary_plane_formats: FormatSet,
|
||||||
|
overlay_plane_formats: FormatSet,
|
||||||
) -> SurfaceDmabufFeedback {
|
) -> SurfaceDmabufFeedback {
|
||||||
let combined_formats = render_formats
|
let combined_formats = render_formats
|
||||||
.intersection(&target_formats)
|
.intersection(&target_formats)
|
||||||
|
|
@ -1494,7 +1494,11 @@ fn get_surface_dmabuf_feedback(
|
||||||
// We limit the scan-out trache to formats we can also render from
|
// We limit the scan-out trache to formats we can also render from
|
||||||
// so that there is always a fallback render path available in case
|
// so that there is always a fallback render path available in case
|
||||||
// the supplied buffer can not be scanned out directly
|
// the supplied buffer can not be scanned out directly
|
||||||
let planes_formats = plane_formats
|
let primary_plane_formats = primary_plane_formats
|
||||||
|
.intersection(&combined_formats)
|
||||||
|
.copied()
|
||||||
|
.collect::<FormatSet>();
|
||||||
|
let overlay_plane_formats = overlay_plane_formats
|
||||||
.intersection(&combined_formats)
|
.intersection(&combined_formats)
|
||||||
.copied()
|
.copied()
|
||||||
.collect::<FormatSet>();
|
.collect::<FormatSet>();
|
||||||
|
|
@ -1514,12 +1518,29 @@ fn get_surface_dmabuf_feedback(
|
||||||
|
|
||||||
let render_feedback = builder.clone().build().unwrap();
|
let render_feedback = builder.clone().build().unwrap();
|
||||||
// we would want to do this in other cases as well, but same thing as above applies
|
// we would want to do this in other cases as well, but same thing as above applies
|
||||||
|
let primary_scanout_feedback = if target_node == render_node {
|
||||||
|
builder
|
||||||
|
.clone()
|
||||||
|
.add_preference_tranche(
|
||||||
|
target_node.dev_id(),
|
||||||
|
Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout),
|
||||||
|
primary_plane_formats.clone(),
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
builder.clone().build().unwrap()
|
||||||
|
};
|
||||||
let scanout_feedback = if target_node == render_node {
|
let scanout_feedback = if target_node == render_node {
|
||||||
builder
|
builder
|
||||||
.add_preference_tranche(
|
.add_preference_tranche(
|
||||||
target_node.dev_id(),
|
target_node.dev_id(),
|
||||||
Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout),
|
Some(zwp_linux_dmabuf_feedback_v1::TrancheFlags::Scanout),
|
||||||
planes_formats,
|
FormatSet::from_iter(
|
||||||
|
primary_plane_formats
|
||||||
|
.into_iter()
|
||||||
|
.chain(overlay_plane_formats.into_iter()),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
|
@ -1530,5 +1551,6 @@ fn get_surface_dmabuf_feedback(
|
||||||
SurfaceDmabufFeedback {
|
SurfaceDmabufFeedback {
|
||||||
render_feedback,
|
render_feedback,
|
||||||
scanout_feedback,
|
scanout_feedback,
|
||||||
|
primary_scanout_feedback,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -552,13 +552,19 @@ impl CosmicSurface {
|
||||||
) where
|
) where
|
||||||
F1: FnMut(&WlSurface, &SurfaceData) -> Option<Output> + Copy,
|
F1: FnMut(&WlSurface, &SurfaceData) -> Option<Output> + Copy,
|
||||||
{
|
{
|
||||||
|
let is_fullscreen = self.is_fullscreen(false);
|
||||||
|
|
||||||
self.0
|
self.0
|
||||||
.send_dmabuf_feedback(output, primary_scan_out_output, |surface, _| {
|
.send_dmabuf_feedback(output, primary_scan_out_output, |surface, _| {
|
||||||
select_dmabuf_feedback(
|
select_dmabuf_feedback(
|
||||||
surface,
|
surface,
|
||||||
render_element_states,
|
render_element_states,
|
||||||
&feedback.render_feedback,
|
&feedback.render_feedback,
|
||||||
&feedback.scanout_feedback,
|
if is_fullscreen {
|
||||||
|
&feedback.primary_scanout_feedback
|
||||||
|
} else {
|
||||||
|
&feedback.scanout_feedback
|
||||||
|
},
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,7 @@ pub enum BackendData {
|
||||||
pub struct SurfaceDmabufFeedback {
|
pub struct SurfaceDmabufFeedback {
|
||||||
pub render_feedback: DmabufFeedback,
|
pub render_feedback: DmabufFeedback,
|
||||||
pub scanout_feedback: DmabufFeedback,
|
pub scanout_feedback: DmabufFeedback,
|
||||||
|
pub primary_scanout_feedback: DmabufFeedback,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -800,7 +801,7 @@ impl Common {
|
||||||
surface,
|
surface,
|
||||||
render_element_states,
|
render_element_states,
|
||||||
&feedback.render_feedback,
|
&feedback.render_feedback,
|
||||||
&feedback.scanout_feedback,
|
&feedback.primary_scanout_feedback,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue