Skip to content

Commit

Permalink
UNTESTED: kms: Allow diverging primary plane formats under certain co…
Browse files Browse the repository at this point in the history
…nditions
  • Loading branch information
Drakulix committed Jan 2, 2025
1 parent 936b5db commit eb73cff
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 39 deletions.
62 changes: 59 additions & 3 deletions src/backend/kms/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ use libc::dev_t;
use smithay::{
backend::{
allocator::{
format::FormatSet,
gbm::{GbmAllocator, GbmDevice},
Fourcc,
Format, Fourcc,
},
drm::{
compositor::FrameFlags, output::DrmOutputManager, DrmDevice, DrmDeviceFd, DrmEvent,
Expand Down Expand Up @@ -612,15 +613,16 @@ impl Device {
}
}

pub fn allow_overlay_scanout(
fn allow_frame_flags(
&mut self,
flag: bool,
flags: FrameFlags,
renderer: &mut GlMultiRenderer,
clock: &Clock<Monotonic>,
shell: &Arc<RwLock<Shell>>,
) -> Result<()> {
for surface in self.surfaces.values_mut() {
surface.allow_overlay_scanout(flag);
surface.allow_frame_flags(flag, flags);
}

if !flag {
Expand Down Expand Up @@ -665,6 +667,60 @@ impl Device {
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,
)
}

pub fn allow_primary_scanout_any(
&mut self,
flag: bool,
renderer: &mut GlMultiRenderer,
clock: &Clock<Monotonic>,
shell: &Arc<RwLock<Shell>>,
) -> Result<()> {
self.allow_frame_flags(
flag,
FrameFlags::ALLOW_PRIMARY_PLANE_SCANOUT_ANY,
renderer,
clock,
shell,
)?;

self.drm.with_compositors(|comps| {
for (crtc, comp) in comps {
let surface = self.surfaces.get_mut(crtc).unwrap();
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 {
Some(&self.render_node) == primary
|| !self.surfaces.is_empty()
Expand Down
22 changes: 22 additions & 0 deletions src/backend/kms/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,28 @@ impl KmsState {

let mut all_outputs = Vec::new();
for device in self.drm_devices.values_mut() {
// configure primary scanout allowance
{
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,
)
.context("Failed to switch primary-plane scanout flags")?;
}

// reconfigure existing
let now = clock.now();
let output_map = device
Expand Down
90 changes: 56 additions & 34 deletions src/backend/kms/surface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,9 @@ pub struct Surface {
known_nodes: HashSet<DrmNode>,

active: Arc<AtomicBool>,
feedback: HashMap<DrmNode, SurfaceDmabufFeedback>,
plane_formats: FormatSet,
pub(super) feedback: HashMap<DrmNode, SurfaceDmabufFeedback>,
pub(super) primary_plane_formats: FormatSet,
overlay_plane_formats: FormatSet,

loop_handle: LoopHandle<'static, State>,
thread_command: Sender<ThreadCommand>,
Expand Down Expand Up @@ -239,7 +240,7 @@ pub enum ThreadCommand {
ScheduleRender,
AdaptiveSyncAvailable(SyncSender<Result<VrrSupport>>),
UseAdaptiveSync(AdaptiveSync),
AllowOverlayScanout(bool, SyncSender<()>),
AllowFrameFlags(bool, FrameFlags, SyncSender<()>),
End,
DpmsOff,
}
Expand Down Expand Up @@ -309,6 +310,7 @@ impl Surface {
.surfaces
.get_mut(&crtc)
.unwrap();

state
.common
.send_dmabuf_feedback(&output_clone, &states, |source_node| {
Expand All @@ -332,7 +334,8 @@ impl Surface {
target_node,
render_formats,
target_formats,
surface.plane_formats.clone(),
surface.primary_plane_formats.clone(),
surface.overlay_plane_formats.clone(),
)
})
.clone(),
Expand All @@ -350,7 +353,8 @@ impl Surface {
known_nodes: HashSet::new(),
active,
feedback: HashMap::new(),
plane_formats: FormatSet::default(),
primary_plane_formats: FormatSet::default(),
overlay_plane_formats: FormatSet::default(),
loop_handle: evlh.clone(),
thread_command: tx,
thread_token,
Expand Down Expand Up @@ -423,11 +427,11 @@ impl Surface {
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 _ = self
.thread_command
.send(ThreadCommand::AllowOverlayScanout(flag, tx));
.send(ThreadCommand::AllowFrameFlags(flag, flags, tx));
let _ = rx.recv();
}

Expand All @@ -439,21 +443,18 @@ impl Surface {

pub fn resume(&mut self, compositor: GbmDrmOutput) -> Result<()> {
let (tx, rx) = std::sync::mpsc::sync_channel(1);
self.plane_formats = compositor.with_compositor(|c| {
c.surface()
.plane_info()
.formats
.iter()
.copied()
.chain(
(self.primary_plane_formats, self.overlay_plane_formats) =
compositor.with_compositor(|c| {
(
c.surface().plane_info().formats.clone(),
c.surface()
.planes()
.overlay
.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 {
compositor,
Expand Down Expand Up @@ -615,20 +616,18 @@ fn surface_thread(
};
}
}
Event::Msg(ThreadCommand::AllowOverlayScanout(flag, tx)) => {
if !crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT").unwrap_or(false)
&& !crate::utils::env::bool_var("COSMIC_DISABLE_OVERLAY_SCANOUT")
.unwrap_or(false)
{
if flag {
state
.frame_flags
.insert(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);
} else {
state
.frame_flags
.remove(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);
}
Event::Msg(ThreadCommand::AllowFrameFlags(flag, mut flags, tx)) => {
if crate::utils::env::bool_var("COSMIC_DISABLE_DIRECT_SCANOUT").unwrap_or(false) {
flags.remove(FrameFlags::ALLOW_SCANOUT);
}
if crate::utils::env::bool_var("COSMIC_DISABLE_OVERLAY_SCANOUT").unwrap_or(false) {
flags.remove(FrameFlags::ALLOW_OVERLAY_PLANE_SCANOUT);
}

if flag {
state.frame_flags.insert(flags);
} else {
state.frame_flags.remove(flags);
}
let _ = tx.send(());
}
Expand Down Expand Up @@ -1484,7 +1483,8 @@ fn get_surface_dmabuf_feedback(
target_node: DrmNode,
render_formats: FormatSet,
target_formats: FormatSet,
plane_formats: FormatSet,
primary_plane_formats: FormatSet,
overlay_plane_formats: FormatSet,
) -> SurfaceDmabufFeedback {
let combined_formats = render_formats
.intersection(&target_formats)
Expand All @@ -1494,7 +1494,11 @@ fn get_surface_dmabuf_feedback(
// 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
// 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)
.copied()
.collect::<FormatSet>();
Expand All @@ -1514,12 +1518,29 @@ fn get_surface_dmabuf_feedback(

let render_feedback = builder.clone().build().unwrap();
// 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 {
builder
.add_preference_tranche(
target_node.dev_id(),
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()
.unwrap()
Expand All @@ -1530,5 +1551,6 @@ fn get_surface_dmabuf_feedback(
SurfaceDmabufFeedback {
render_feedback,
scanout_feedback,
primary_scanout_feedback,
}
}
6 changes: 5 additions & 1 deletion src/shell/element/surface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,11 @@ impl CosmicSurface {
surface,
render_element_states,
&feedback.render_feedback,
&feedback.scanout_feedback,
if self.is_fullscreen(false) {
&feedback.primary_scanout_feedback
} else {
&feedback.scanout_feedback
},
)
})
}
Expand Down
3 changes: 2 additions & 1 deletion src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ pub enum BackendData {
pub struct SurfaceDmabufFeedback {
pub render_feedback: DmabufFeedback,
pub scanout_feedback: DmabufFeedback,
pub primary_scanout_feedback: DmabufFeedback,
}

#[derive(Debug)]
Expand Down Expand Up @@ -800,7 +801,7 @@ impl Common {
surface,
render_element_states,
&feedback.render_feedback,
&feedback.scanout_feedback,
&feedback.primary_scanout_feedback,
)
},
)
Expand Down

0 comments on commit eb73cff

Please # to comment.