drm: Properly expose planes for surfaces

This commit is contained in:
Victor Brekenfeld 2021-05-20 01:06:16 +02:00
parent fd2703a57d
commit a4f66da69f
9 changed files with 552 additions and 294 deletions

View File

@ -330,15 +330,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
.collect::<Vec<EncoderInfo>>(); .collect::<Vec<EncoderInfo>>();
'outer: for encoder_info in encoder_infos { 'outer: for encoder_info in encoder_infos {
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) { for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
// TODO cursor
let primary = match device.planes(&crtc) {
Ok(planes) => planes.primary,
Err(err) => {
warn!(logger, "Failed to enumerate planes: {}", err);
continue;
}
};
if let Entry::Vacant(entry) = backends.entry(crtc) { if let Entry::Vacant(entry) = backends.entry(crtc) {
info!( info!(
logger, logger,
@ -363,7 +354,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
}; };
let mut surface = match device.create_surface( let mut surface = match device.create_surface(
crtc, crtc,
primary,
connector_info.modes()[0], connector_info.modes()[0],
&[connector_info.handle()], &[connector_info.handle()],
) { ) {

View File

@ -79,13 +79,10 @@ fn main() {
// Assuming we found a good connector and loaded the info into `connector_info` // Assuming we found a good connector and loaded the info into `connector_info`
let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.) let mode = connector_info.modes()[0]; // Use first mode (usually highest resoltion, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
// We just use one plane, the primary one
let plane = device.planes(&crtc).unwrap().primary;
// Initialize the hardware backend // Initialize the hardware backend
let surface = Rc::new( let surface = Rc::new(
device device
.create_surface(crtc, plane, mode, &[connector_info.handle()]) .create_surface(crtc, mode, &[connector_info.handle()])
.unwrap(), .unwrap(),
); );
@ -125,7 +122,7 @@ fn main() {
.unwrap(); .unwrap();
// Start rendering // Start rendering
surface.commit(framebuffer, true).unwrap(); surface.commit([(framebuffer, surface.plane())].iter(), true).unwrap();
// Run // Run
event_loop.run(None, &mut (), |_| {}).unwrap(); event_loop.run(None, &mut (), |_| {}).unwrap();
@ -161,7 +158,7 @@ impl DeviceHandler for DrmHandlerImpl {
} }
let fb = self.current.userdata().unwrap(); let fb = self.current.userdata().unwrap();
self.surface.page_flip(fb, true).unwrap(); self.surface.page_flip([(fb, self.surface.plane())].iter(), true).unwrap();
} }
fn error(&mut self, error: DrmError) { fn error(&mut self, error: DrmError) {

View File

@ -181,6 +181,11 @@ impl<A: AsRawFd + 'static> AtomicDrmDevice<A> {
dev: self.fd.dev_path(), dev: self.fd.dev_path(),
source, source,
})?; })?;
let plane_handles = self.fd.plane_handles().map_err(|source| Error::Access {
errmsg: "Error loading drm plane resources",
dev: self.fd.dev_path(),
source,
})?;
// Disable all connectors (otherwise we might run into conflicting commits when restarting the rendering loop) // Disable all connectors (otherwise we might run into conflicting commits when restarting the rendering loop)
let mut req = AtomicModeReq::new(); let mut req = AtomicModeReq::new();
@ -194,6 +199,26 @@ impl<A: AsRawFd + 'static> AtomicDrmDevice<A> {
.expect("Unknown property CRTC_ID"); .expect("Unknown property CRTC_ID");
req.add_property(*conn, *prop, property::Value::CRTC(None)); req.add_property(*conn, *prop, property::Value::CRTC(None));
} }
// Disable all planes
for plane in plane_handles.planes() {
let prop = self
.prop_mapping
.3
.get(&plane)
.expect("Unknown handle")
.get("CRTC_ID")
.expect("Unknown property CRTC_ID");
req.add_property(*plane, *prop, property::Value::CRTC(None));
let prop = self
.prop_mapping
.3
.get(&plane)
.expect("Unknown handle")
.get("FB_ID")
.expect("Unknown property FB_ID");
req.add_property(*plane, *prop, property::Value::Framebuffer(None));
}
// A crtc without a connector has no mode, we also need to reset that. // A crtc without a connector has no mode, we also need to reset that.
// Otherwise the commit will not be accepted. // Otherwise the commit will not be accepted.
for crtc in res_handles.crtcs() { for crtc in res_handles.crtcs() {

View File

@ -89,6 +89,8 @@ impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
set_connector_state(&*self.fd, res_handles.connectors().iter().copied(), false)?; set_connector_state(&*self.fd, res_handles.connectors().iter().copied(), false)?;
for crtc in res_handles.crtcs() { for crtc in res_handles.crtcs() {
#[allow(deprecated)]
let _ = self.fd.set_cursor(*crtc, Option::<&drm::control::dumbbuffer::DumbBuffer>::None);
// null commit (necessary to trigger removal on the kernel side with the legacy api.) // null commit (necessary to trigger removal on the kernel side with the legacy api.)
self.fd self.fd
.set_crtc(*crtc, None, (0, 0), &[], None) .set_crtc(*crtc, None, (0, 0), &[], None)

View File

@ -1,6 +1,4 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashSet;
use std::convert::TryFrom;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc; use std::rc::Rc;
@ -8,18 +6,17 @@ use std::sync::{atomic::AtomicBool, Arc};
use calloop::{generic::Generic, InsertError, LoopHandle, Source}; use calloop::{generic::Generic, InsertError, LoopHandle, Source};
use drm::control::{ use drm::control::{
connector, crtc, plane, property, Device as ControlDevice, Event, Mode, PlaneResourceHandles, PlaneType, connector, crtc, Device as ControlDevice, Event, Mode,
ResourceHandles, ResourceHandles,
}; };
use drm::{ClientCapability, Device as BasicDevice, DriverCapability}; use drm::{ClientCapability, Device as BasicDevice};
use nix::libc::dev_t; use nix::libc::dev_t;
use nix::sys::stat::fstat; use nix::sys::stat::fstat;
pub(super) mod atomic; pub(super) mod atomic;
pub(super) mod legacy; pub(super) mod legacy;
use super::error::Error; use super::{error::Error, Planes, planes};
use super::surface::{atomic::AtomicDrmSurface, legacy::LegacyDrmSurface, DrmSurface, DrmSurfaceInternal}; use super::surface::{atomic::AtomicDrmSurface, legacy::LegacyDrmSurface, DrmSurface, DrmSurfaceInternal};
use crate::backend::allocator::{Format, Fourcc, Modifier};
use atomic::AtomicDrmDevice; use atomic::AtomicDrmDevice;
use legacy::LegacyDrmDevice; use legacy::LegacyDrmDevice;
@ -32,7 +29,6 @@ pub struct DrmDevice<A: AsRawFd + 'static> {
pub(super) links: RefCell<Vec<crate::signaling::SignalToken>>, pub(super) links: RefCell<Vec<crate::signaling::SignalToken>>,
has_universal_planes: bool, has_universal_planes: bool,
resources: ResourceHandles, resources: ResourceHandles,
planes: PlaneResourceHandles,
pub(super) logger: ::slog::Logger, pub(super) logger: ::slog::Logger,
} }
@ -142,11 +138,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
dev: dev.dev_path(), dev: dev.dev_path(),
source, source,
})?; })?;
let planes = dev.plane_handles().map_err(|source| Error::Access {
errmsg: "Error loading plane handles",
dev: dev.dev_path(),
source,
})?;
let internal = Arc::new(DrmDevice::create_internal( let internal = Arc::new(DrmDevice::create_internal(
dev, dev,
active, active,
@ -162,7 +153,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
links: RefCell::new(Vec::new()), links: RefCell::new(Vec::new()),
has_universal_planes, has_universal_planes,
resources, resources,
planes,
logger: log, logger: log,
}) })
} }
@ -258,65 +248,7 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
/// Returns a set of available planes for a given crtc /// Returns a set of available planes for a given crtc
pub fn planes(&self, crtc: &crtc::Handle) -> Result<Planes, Error> { pub fn planes(&self, crtc: &crtc::Handle) -> Result<Planes, Error> {
let mut primary = None; planes(self, crtc, self.has_universal_planes)
let mut cursor = None;
let mut overlay = Vec::new();
for plane in self.planes.planes() {
let info = self.get_plane(*plane).map_err(|source| Error::Access {
errmsg: "Failed to get plane information",
dev: self.dev_path(),
source,
})?;
let filter = info.possible_crtcs();
if self.resources.filter_crtcs(filter).contains(crtc) {
match self.plane_type(*plane)? {
PlaneType::Primary => {
primary = Some(*plane);
}
PlaneType::Cursor => {
cursor = Some(*plane);
}
PlaneType::Overlay => {
overlay.push(*plane);
}
};
}
}
Ok(Planes {
primary: primary.expect("Crtc has no primary plane"),
cursor,
overlay: if self.has_universal_planes {
Some(overlay)
} else {
None
},
})
}
fn plane_type(&self, plane: plane::Handle) -> Result<PlaneType, Error> {
let props = self.get_properties(plane).map_err(|source| Error::Access {
errmsg: "Failed to get properties of plane",
dev: self.dev_path(),
source,
})?;
let (ids, vals) = props.as_props_and_values();
for (&id, &val) in ids.iter().zip(vals.iter()) {
let info = self.get_property(id).map_err(|source| Error::Access {
errmsg: "Failed to get property info",
dev: self.dev_path(),
source,
})?;
if info.name().to_str().map(|x| x == "type").unwrap_or(false) {
return Ok(match val {
x if x == (PlaneType::Primary as u64) => PlaneType::Primary,
x if x == (PlaneType::Cursor as u64) => PlaneType::Cursor,
_ => PlaneType::Overlay,
});
}
}
unreachable!()
} }
/// Creates a new rendering surface. /// Creates a new rendering surface.
@ -338,7 +270,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
pub fn create_surface( pub fn create_surface(
&self, &self,
crtc: crtc::Handle, crtc: crtc::Handle,
plane: plane::Handle,
mode: Mode, mode: Mode,
connectors: &[connector::Handle], connectors: &[connector::Handle],
) -> Result<DrmSurface<A>, Error> { ) -> Result<DrmSurface<A>, Error> {
@ -346,6 +277,7 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
return Err(Error::SurfaceWithoutConnectors(crtc)); return Err(Error::SurfaceWithoutConnectors(crtc));
} }
let plane = planes(self, &crtc, self.has_universal_planes)?.primary;
let info = self.get_plane(plane).map_err(|source| Error::Access { let info = self.get_plane(plane).map_err(|source| Error::Access {
errmsg: "Failed to get plane info", errmsg: "Failed to get plane info",
dev: self.dev_path(), dev: self.dev_path(),
@ -378,10 +310,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
self.logger.clone(), self.logger.clone(),
)?) )?)
} else { } else {
if self.plane_type(plane)? != PlaneType::Primary {
return Err(Error::NonPrimaryPlane(plane));
}
DrmSurfaceInternal::Legacy(LegacyDrmSurface::new( DrmSurfaceInternal::Legacy(LegacyDrmSurface::new(
self.internal.clone(), self.internal.clone(),
active, active,
@ -392,131 +320,12 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
)?) )?)
}; };
// get plane formats
let plane_info = self.get_plane(plane).map_err(|source| Error::Access {
errmsg: "Error loading plane info",
dev: self.dev_path(),
source,
})?;
let mut formats = HashSet::new();
for code in plane_info
.formats()
.iter()
.flat_map(|x| Fourcc::try_from(*x).ok())
{
formats.insert(Format {
code,
modifier: Modifier::Invalid,
});
}
if let Ok(1) = self.get_driver_capability(DriverCapability::AddFB2Modifiers) {
let set = self.get_properties(plane).map_err(|source| Error::Access {
errmsg: "Failed to query properties",
dev: self.dev_path(),
source,
})?;
let (handles, _) = set.as_props_and_values();
// for every handle ...
let prop = handles
.iter()
.find(|handle| {
// get information of that property
if let Some(info) = self.get_property(**handle).ok() {
// to find out, if we got the handle of the "IN_FORMATS" property ...
if info.name().to_str().map(|x| x == "IN_FORMATS").unwrap_or(false) {
// so we can use that to get formats
return true;
}
}
false
})
.copied();
if let Some(prop) = prop {
let prop_info = self.get_property(prop).map_err(|source| Error::Access {
errmsg: "Failed to query property",
dev: self.dev_path(),
source,
})?;
let (handles, raw_values) = set.as_props_and_values();
let raw_value = raw_values[handles
.iter()
.enumerate()
.find_map(|(i, handle)| if *handle == prop { Some(i) } else { None })
.unwrap()];
if let property::Value::Blob(blob) = prop_info.value_type().convert_value(raw_value) {
let data = self.get_property_blob(blob).map_err(|source| Error::Access {
errmsg: "Failed to query property blob data",
dev: self.dev_path(),
source,
})?;
// be careful here, we have no idea about the alignment inside the blob, so always copy using `read_unaligned`,
// although slice::from_raw_parts would be so much nicer to iterate and to read.
unsafe {
let fmt_mod_blob_ptr = data.as_ptr() as *const drm_ffi::drm_format_modifier_blob;
let fmt_mod_blob = &*fmt_mod_blob_ptr;
let formats_ptr: *const u32 = fmt_mod_blob_ptr
.cast::<u8>()
.offset(fmt_mod_blob.formats_offset as isize)
as *const _;
let modifiers_ptr: *const drm_ffi::drm_format_modifier = fmt_mod_blob_ptr
.cast::<u8>()
.offset(fmt_mod_blob.modifiers_offset as isize)
as *const _;
let formats_ptr = formats_ptr as *const u32;
let modifiers_ptr = modifiers_ptr as *const drm_ffi::drm_format_modifier;
for i in 0..fmt_mod_blob.count_modifiers {
let mod_info = modifiers_ptr.offset(i as isize).read_unaligned();
for j in 0..64 {
if mod_info.formats & (1u64 << j) != 0 {
let code = Fourcc::try_from(
formats_ptr
.offset((j + mod_info.offset) as isize)
.read_unaligned(),
)
.ok();
let modifier = Modifier::from(mod_info.modifier);
if let Some(code) = code {
formats.insert(Format { code, modifier });
}
}
}
}
}
}
}
} else if self.plane_type(plane)? == PlaneType::Cursor {
// Force a LINEAR layout for the cursor if the driver doesn't support modifiers
for format in formats.clone() {
formats.insert(Format {
code: format.code,
modifier: Modifier::Linear,
});
}
}
if formats.is_empty() {
formats.insert(Format {
code: Fourcc::Argb8888,
modifier: Modifier::Invalid,
});
}
trace!(
self.logger,
"Supported scan-out formats for plane ({:?}): {:?}",
plane,
formats
);
Ok(DrmSurface { Ok(DrmSurface {
dev_id: self.dev_id, dev_id: self.dev_id,
crtc, crtc,
plane, primary: plane,
internal: Arc::new(internal), internal: Arc::new(internal),
formats, has_universal_planes: self.has_universal_planes,
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
links: RefCell::new(Vec::new()), links: RefCell::new(Vec::new()),
}) })
@ -528,15 +337,7 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
} }
} }
/// A set of planes as supported by a crtc
pub struct Planes {
/// The primary plane of the crtc
pub primary: plane::Handle,
/// The cursor plane of the crtc, if available
pub cursor: Option<plane::Handle>,
/// Overlay planes supported by the crtc, if available
pub overlay: Option<Vec<plane::Handle>>,
}
/// Trait to receive events of a bound [`DrmDevice`] /// Trait to receive events of a bound [`DrmDevice`]
/// ///

View File

@ -68,10 +68,100 @@ mod render;
pub(self) mod session; pub(self) mod session;
pub(self) mod surface; pub(self) mod surface;
pub use device::{device_bind, DevPath, DeviceHandler, DrmDevice, DrmSource, Planes}; pub use device::{device_bind, DevPath, DeviceHandler, DrmDevice, DrmSource};
pub use error::Error as DrmError; pub use error::Error as DrmError;
#[cfg(feature = "backend_gbm")] #[cfg(feature = "backend_gbm")]
pub use render::{DrmRenderSurface, Error as DrmRenderError}; pub use render::{DrmRenderSurface, Error as DrmRenderError};
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
pub use session::{DrmDeviceObserver, DrmSurfaceObserver}; pub use session::{DrmDeviceObserver, DrmSurfaceObserver};
pub use surface::DrmSurface; pub use surface::DrmSurface;
use drm::control::{plane, crtc, Device as ControlDevice, PlaneType};
/// A set of planes as supported by a crtc
pub struct Planes {
/// The primary plane of the crtc (automatically selected for [DrmDevice::create_surface])
pub primary: plane::Handle,
/// The cursor plane of the crtc, if available
pub cursor: Option<plane::Handle>,
/// Overlay planes supported by the crtc, if available
pub overlay: Option<Vec<plane::Handle>>,
}
fn planes(dev: &impl ControlDevice, crtc: &crtc::Handle, has_universal_planes: bool) -> Result<Planes, DrmError> {
let mut primary = None;
let mut cursor = None;
let mut overlay = Vec::new();
let planes = dev.plane_handles().map_err(|source| DrmError::Access {
errmsg: "Error loading plane handles",
dev: dev.dev_path(),
source,
})?;
let resources = dev.resource_handles().map_err(|source| DrmError::Access {
errmsg: "Error loading resource handles",
dev: dev.dev_path(),
source,
})?;
for plane in planes.planes() {
let info = dev.get_plane(*plane).map_err(|source| DrmError::Access {
errmsg: "Failed to get plane information",
dev: dev.dev_path(),
source,
})?;
let filter = info.possible_crtcs();
if resources.filter_crtcs(filter).contains(crtc) {
match plane_type(dev, *plane)? {
PlaneType::Primary => {
primary = Some(*plane);
}
PlaneType::Cursor => {
cursor = Some(*plane);
}
PlaneType::Overlay => {
overlay.push(*plane);
}
};
}
}
Ok(Planes {
primary: primary.expect("Crtc has no primary plane"),
cursor: if has_universal_planes {
cursor
} else {
None
},
overlay: if has_universal_planes && !overlay.is_empty() {
Some(overlay)
} else {
None
},
})
}
fn plane_type(dev: &impl ControlDevice, plane: plane::Handle) -> Result<PlaneType, DrmError> {
let props = dev.get_properties(plane).map_err(|source| DrmError::Access {
errmsg: "Failed to get properties of plane",
dev: dev.dev_path(),
source,
})?;
let (ids, vals) = props.as_props_and_values();
for (&id, &val) in ids.iter().zip(vals.iter()) {
let info = dev.get_property(id).map_err(|source| DrmError::Access {
errmsg: "Failed to get property info",
dev: dev.dev_path(),
source,
})?;
if info.name().to_str().map(|x| x == "type").unwrap_or(false) {
return Ok(match val {
x if x == (PlaneType::Primary as u64) => PlaneType::Primary,
x if x == (PlaneType::Cursor as u64) => PlaneType::Cursor,
_ => PlaneType::Overlay,
});
}
}
unreachable!()
}

View File

@ -79,7 +79,7 @@ where
// select a format // select a format
let plane_formats = drm let plane_formats = drm
.supported_formats() .supported_formats(drm.plane())?
.iter() .iter()
.filter(|fmt| fmt.code == code) .filter(|fmt| fmt.code == code)
.cloned() .cloned()
@ -517,9 +517,9 @@ where
.fb; .fb;
let flip = if self.drm.commit_pending() { let flip = if self.drm.commit_pending() {
self.drm.commit(fb, true) self.drm.commit([(fb, self.drm.plane())].iter(), true)
} else { } else {
self.drm.page_flip(fb, true) self.drm.page_flip([(fb, self.drm.plane())].iter(), true)
}; };
if flip.is_ok() { if flip.is_ok() {
self.pending_fb = Some(slot); self.pending_fb = Some(slot);

View File

@ -96,11 +96,21 @@ impl State {
} }
} }
#[derive(Debug, Clone)]
pub struct PlaneInfo {
handle: plane::Handle,
x: i32,
y: i32,
w: u32,
h: u32,
}
pub struct AtomicDrmSurface<A: AsRawFd + 'static> { pub struct AtomicDrmSurface<A: AsRawFd + 'static> {
pub(super) fd: Arc<DrmDeviceInternal<A>>, pub(super) fd: Arc<DrmDeviceInternal<A>>,
pub(super) active: Arc<AtomicBool>, pub(super) active: Arc<AtomicBool>,
crtc: crtc::Handle, crtc: crtc::Handle,
plane: plane::Handle, plane: plane::Handle,
additional_planes: Mutex<Vec<PlaneInfo>>,
prop_mapping: Mapping, prop_mapping: Mapping,
state: RwLock<State>, state: RwLock<State>,
pending: RwLock<State>, pending: RwLock<State>,
@ -147,6 +157,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
active, active,
crtc, crtc,
plane, plane,
additional_planes: Mutex::new(Vec::new()),
prop_mapping, prop_mapping,
state: RwLock::new(state), state: RwLock::new(state),
pending: RwLock::new(pending), pending: RwLock::new(pending),
@ -159,8 +170,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
// we need a framebuffer to do test commits, which we use to verify our pending state. // we need a framebuffer to do test commits, which we use to verify our pending state.
// here we create a dumbbuffer for that purpose. // here we create a dumbbuffer for that purpose.
fn create_test_buffer(&self, mode: &Mode) -> Result<framebuffer::Handle, Error> { fn create_test_buffer(&self, size: (u16, u16)) -> Result<framebuffer::Handle, Error> {
let (w, h) = mode.size(); let (w, h) = size;
let db = self let db = self
.fd .fd
.create_dumb_buffer( .create_dumb_buffer(
@ -228,7 +239,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
&mut [conn].iter(), &mut [conn].iter(),
&mut [].iter(), &mut [].iter(),
self.plane, self.plane,
Some(self.create_test_buffer(&pending.mode)?), &[],
Some([(self.create_test_buffer(pending.mode.size())?, self.plane)].iter()),
Some(pending.mode), Some(pending.mode),
Some(pending.blob), Some(pending.blob),
)?; )?;
@ -265,7 +277,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
&mut [].iter(), &mut [].iter(),
&mut [conn].iter(), &mut [conn].iter(),
self.plane, self.plane,
Some(self.create_test_buffer(&pending.mode)?), &[],
Some([(self.create_test_buffer(pending.mode.size())?, self.plane)].iter()),
Some(pending.mode), Some(pending.mode),
Some(pending.blob), Some(pending.blob),
)?; )?;
@ -303,7 +316,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
&mut added, &mut added,
&mut removed, &mut removed,
self.plane, self.plane,
Some(self.create_test_buffer(&pending.mode)?), &[],
Some([(self.create_test_buffer(pending.mode.size())?, self.plane)].iter()),
Some(pending.mode), Some(pending.mode),
Some(pending.blob), Some(pending.blob),
)?; )?;
@ -337,12 +351,13 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
source, source,
})?; })?;
let test_fb = Some(self.create_test_buffer(&pending.mode)?); let test_fb = self.create_test_buffer(pending.mode.size())?;
let req = self.build_request( let req = self.build_request(
&mut pending.connectors.iter(), &mut pending.connectors.iter(),
&mut [].iter(), &mut [].iter(),
self.plane, self.plane,
test_fb, &[],
Some([(test_fb, self.plane)].iter()),
Some(mode), Some(mode),
Some(new_blob), Some(new_blob),
)?; )?;
@ -365,11 +380,55 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
Ok(()) Ok(())
} }
pub fn use_plane(&self, plane: plane::Handle, position: (i32, i32), size: (u32, u32)) -> Result<(), Error> {
let info = PlaneInfo {
handle: plane,
x: position.0,
y: position.1,
w: size.0,
h: size.1,
};
let mut planes = self.additional_planes.lock().unwrap();
let mut new_planes = planes.clone();
new_planes.push(info);
let pending = self.pending.write().unwrap();
let req = self.build_request(
&mut pending.connectors.iter(),
&mut [].iter(),
self.plane,
&new_planes,
Some([(self.create_test_buffer(pending.mode.size())?, self.plane)]
.iter()
.chain(new_planes.iter().map(|info| {
match self.create_test_buffer((info.w as u16, info.h as u16)) {
Ok(test_buff) => Ok((test_buff, info.handle)),
Err(err) => Err(err),
}
}).collect::<Result<Vec<_>, _>>()?.iter())
),
Some(pending.mode),
Some(pending.blob),
)?;
self
.fd
.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req,
)
.map_err(|_| Error::TestFailed(self.crtc))?;
*planes = new_planes;
Ok(())
}
pub fn commit_pending(&self) -> bool { pub fn commit_pending(&self) -> bool {
*self.pending.read().unwrap() != *self.state.read().unwrap() *self.pending.read().unwrap() != *self.state.read().unwrap()
} }
pub fn commit(&self, framebuffer: framebuffer::Handle, event: bool) -> Result<(), Error> { pub fn commit<'a>(&self, framebuffers: impl Iterator<Item=&'a (framebuffer::Handle, plane::Handle)>, event: bool) -> Result<(), Error> {
if !self.active.load(Ordering::SeqCst) { if !self.active.load(Ordering::SeqCst) {
return Err(Error::DeviceInactive); return Err(Error::DeviceInactive);
} }
@ -416,7 +475,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
&mut added, &mut added,
&mut removed, &mut removed,
self.plane, self.plane,
Some(framebuffer), &*self.additional_planes.lock().unwrap(),
Some(framebuffers),
Some(pending.mode), Some(pending.mode),
Some(pending.blob), Some(pending.blob),
)?; )?;
@ -478,7 +538,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
result result
} }
pub fn page_flip(&self, framebuffer: framebuffer::Handle, event: bool) -> Result<(), Error> { pub fn page_flip<'a>(&self, framebuffers: impl Iterator<Item=&'a (framebuffer::Handle, plane::Handle)>, event: bool) -> Result<(), Error> {
if !self.active.load(Ordering::SeqCst) { if !self.active.load(Ordering::SeqCst) {
return Err(Error::DeviceInactive); return Err(Error::DeviceInactive);
} }
@ -488,7 +548,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
&mut [].iter(), &mut [].iter(),
&mut [].iter(), &mut [].iter(),
self.plane, self.plane,
Some(framebuffer), &*self.additional_planes.lock().unwrap(),
Some(framebuffers),
None, None,
None, None,
)?; )?;
@ -541,7 +602,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
&mut added, &mut added,
&mut removed, &mut removed,
self.plane, self.plane,
Some(fb), &[],
Some([(fb, self.plane)].iter()),
Some(*mode), Some(*mode),
Some(blob), Some(blob),
)?; )?;
@ -556,6 +618,38 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
Ok(result) Ok(result)
} }
pub fn test_plane_buffer(&self, fb: framebuffer::Handle, plane: plane::Handle, position: (i32, i32), size: (u32, u32)) -> Result<bool, Error> {
if !self.active.load(Ordering::SeqCst) {
return Err(Error::DeviceInactive);
}
let pending = self.pending.read().unwrap();
let req = self.build_request(
&mut pending.connectors.iter(),
&mut [].iter(),
self.plane,
&[PlaneInfo {
handle: plane,
x: position.0,
y: position.1,
w: size.0,
h: size.1,
}],
Some([(fb, self.plane), (fb, plane)].iter()),
Some(pending.mode),
Some(pending.blob),
)?;
let result = self
.fd
.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req,
)
.is_ok();
Ok(result)
}
pub(crate) fn conn_prop_handle( pub(crate) fn conn_prop_handle(
&self, &self,
handle: connector::Handle, handle: connector::Handle,
@ -626,12 +720,13 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
} }
// If a mode is set a matching blob needs to be set (the inverse is not true) // If a mode is set a matching blob needs to be set (the inverse is not true)
pub fn build_request( pub fn build_request<'a>(
&self, &self,
new_connectors: &mut dyn Iterator<Item = &connector::Handle>, new_connectors: &mut dyn Iterator<Item = &connector::Handle>,
removed_connectors: &mut dyn Iterator<Item = &connector::Handle>, removed_connectors: &mut dyn Iterator<Item = &connector::Handle>,
plane: plane::Handle, primary: plane::Handle,
framebuffer: Option<framebuffer::Handle>, planes: &[PlaneInfo],
framebuffers: Option<impl Iterator<Item=&'a (framebuffer::Handle, plane::Handle)>>,
mode: Option<Mode>, mode: Option<Mode>,
blob: Option<property::Value<'static>>, blob: Option<property::Value<'static>>,
) -> Result<AtomicModeReq, Error> { ) -> Result<AtomicModeReq, Error> {
@ -674,68 +769,116 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
property::Value::Boolean(true), property::Value::Boolean(true),
); );
// and we need to set the framebuffer for our primary plane // and we need to set the framebuffers for our planes
if let Some(fb) = framebuffer { if let Some(fbs) = framebuffers {
req.add_property( for (fb, plane) in fbs {
plane, req.add_property(
self.plane_prop_handle(plane, "FB_ID")?, *plane,
property::Value::Framebuffer(Some(fb)), self.plane_prop_handle(*plane, "FB_ID")?,
); property::Value::Framebuffer(Some(*fb)),
);
}
} }
// we also need to connect the plane // we also need to connect the primary plane
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "CRTC_ID")?, self.plane_prop_handle(primary, "CRTC_ID")?,
property::Value::CRTC(Some(self.crtc)), property::Value::CRTC(Some(self.crtc)),
); );
// if there is a new mode, we should also make sure the plane is sized correctly // if there is a new mode, we should also make sure the primary plane is sized correctly
if let Some(mode) = mode { if let Some(mode) = mode {
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "SRC_X")?, self.plane_prop_handle(primary, "SRC_X")?,
property::Value::UnsignedRange(0), property::Value::UnsignedRange(0),
); );
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "SRC_Y")?, self.plane_prop_handle(primary, "SRC_Y")?,
property::Value::UnsignedRange(0), property::Value::UnsignedRange(0),
); );
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "SRC_W")?, self.plane_prop_handle(primary, "SRC_W")?,
// these are 16.16. fixed point // these are 16.16. fixed point
property::Value::UnsignedRange((mode.size().0 as u64) << 16), property::Value::UnsignedRange((mode.size().0 as u64) << 16),
); );
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "SRC_H")?, self.plane_prop_handle(primary, "SRC_H")?,
property::Value::UnsignedRange((mode.size().1 as u64) << 16), property::Value::UnsignedRange((mode.size().1 as u64) << 16),
); );
// we can map parts of the plane onto different coordinated on the crtc, but we just use a 1:1 mapping. // we can map parts of the plane onto different coordinated on the crtc, but we just use a 1:1 mapping.
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "CRTC_X")?, self.plane_prop_handle(primary, "CRTC_X")?,
property::Value::SignedRange(0), property::Value::SignedRange(0),
); );
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "CRTC_Y")?, self.plane_prop_handle(primary, "CRTC_Y")?,
property::Value::SignedRange(0), property::Value::SignedRange(0),
); );
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "CRTC_W")?, self.plane_prop_handle(primary, "CRTC_W")?,
property::Value::UnsignedRange(mode.size().0 as u64), property::Value::UnsignedRange(mode.size().0 as u64),
); );
req.add_property( req.add_property(
plane, primary,
self.plane_prop_handle(plane, "CRTC_H")?, self.plane_prop_handle(primary, "CRTC_H")?,
property::Value::UnsignedRange(mode.size().1 as u64), property::Value::UnsignedRange(mode.size().1 as u64),
); );
} }
// and finally the others
for plane_info in planes {
req.add_property(
plane_info.handle,
self.plane_prop_handle(plane_info.handle, "SRC_X")?,
property::Value::UnsignedRange(0),
);
req.add_property(
plane_info.handle,
self.plane_prop_handle(plane_info.handle, "SRC_Y")?,
property::Value::UnsignedRange(0),
);
req.add_property(
plane_info.handle,
self.plane_prop_handle(plane_info.handle, "SRC_W")?,
// these are 16.16. fixed point
property::Value::UnsignedRange((plane_info.w as u64) << 16),
);
req.add_property(
plane_info.handle,
self.plane_prop_handle(plane_info.handle, "SRC_H")?,
property::Value::UnsignedRange((plane_info.h as u64) << 16),
);
// we can map parts of the plane onto different coordinated on the crtc, but we just use a 1:1 mapping.
req.add_property(
plane_info.handle,
self.plane_prop_handle(plane_info.handle, "CRTC_X")?,
property::Value::SignedRange(plane_info.x as i64),
);
req.add_property(
plane_info.handle,
self.plane_prop_handle(plane_info.handle, "CRTC_Y")?,
property::Value::SignedRange(plane_info.y as i64),
);
req.add_property(
plane_info.handle,
self.plane_prop_handle(plane_info.handle, "CRTC_W")?,
property::Value::UnsignedRange(plane_info.w as u64),
);
req.add_property(
plane_info.handle,
self.plane_prop_handle(plane_info.handle, "CRTC_H")?,
property::Value::UnsignedRange(plane_info.h as u64),
);
}
Ok(req) Ok(req)
} }
@ -743,7 +886,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
// this is mostly used to remove the contents quickly, e.g. on tty switch, // this is mostly used to remove the contents quickly, e.g. on tty switch,
// as other compositors might not make use of other planes, // as other compositors might not make use of other planes,
// leaving our e.g. cursor or overlays as a relict of a better time on the screen. // leaving our e.g. cursor or overlays as a relict of a better time on the screen.
pub fn clear_plane(&self) -> Result<(), Error> { pub fn clear_plane(&self, plane: plane::Handle) -> Result<(), Error> {
let mut req = AtomicModeReq::new(); let mut req = AtomicModeReq::new();
req.add_property( req.add_property(
@ -758,17 +901,19 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
property::Value::Framebuffer(None), property::Value::Framebuffer(None),
); );
self.fd let result = self.fd
.atomic_commit(&[AtomicCommitFlags::TestOnly], req.clone())
.map_err(|_| Error::TestFailed(self.crtc))?;
self.fd
.atomic_commit(&[AtomicCommitFlags::Nonblock], req) .atomic_commit(&[AtomicCommitFlags::Nonblock], req)
.map_err(|source| Error::Access { .map_err(|source| Error::Access {
errmsg: "Failed to commit on clear_plane", errmsg: "Failed to commit on clear_plane",
dev: self.fd.dev_path(), dev: self.fd.dev_path(),
source, source,
}) });
if result.is_ok() {
self.additional_planes.lock().unwrap().retain(|info| info.handle != plane);
}
result
} }
pub(crate) fn reset_state<B: AsRawFd + ControlDevice + 'static>( pub(crate) fn reset_state<B: AsRawFd + ControlDevice + 'static>(
@ -801,8 +946,13 @@ impl<A: AsRawFd + 'static> Drop for AtomicDrmSurface<A> {
// other ttys that use no cursor, might not clear it themselves. // other ttys that use no cursor, might not clear it themselves.
// This makes sure our cursor won't stay visible. // This makes sure our cursor won't stay visible.
if let Err(err) = self.clear_plane() { if let Err(err) = self.clear_plane(self.plane) {
warn!(self.logger, "Failed to clear plane on {:?}: {}", self.crtc, err); warn!(self.logger, "Failed to clear plane {:?} on {:?}: {}", self.plane, self.crtc, err);
}
for plane_info in self.additional_planes.lock().unwrap().iter() {
if let Err(err) = self.clear_plane(plane_info.handle) {
warn!(self.logger, "Failed to clear plane {:?} on {:?}: {}", plane_info.handle, self.crtc, err);
}
} }
// disable connectors again // disable connectors again

View File

@ -1,17 +1,23 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashSet; use std::collections::HashSet;
use std::convert::TryFrom;
use std::os::unix::io::{AsRawFd, RawFd}; use std::os::unix::io::{AsRawFd, RawFd};
use std::sync::Arc; use std::sync::Arc;
use drm::control::{connector, crtc, framebuffer, plane, Device as ControlDevice, Mode}; use drm::control::{connector, crtc, framebuffer, plane, property, Device as ControlDevice, Mode};
use drm::Device as BasicDevice; use drm::{Device as BasicDevice, DriverCapability};
use nix::libc::dev_t; use nix::libc::dev_t;
pub(super) mod atomic; pub(super) mod atomic;
pub(super) mod legacy; pub(super) mod legacy;
use super::error::Error; use super::{
use crate::backend::allocator::Format; error::Error,
Planes, PlaneType,
plane_type, planes,
device::DevPath,
};
use crate::backend::allocator::{Format, Fourcc, Modifier};
use atomic::AtomicDrmSurface; use atomic::AtomicDrmSurface;
use legacy::LegacyDrmSurface; use legacy::LegacyDrmSurface;
@ -19,9 +25,9 @@ use legacy::LegacyDrmSurface;
pub struct DrmSurface<A: AsRawFd + 'static> { pub struct DrmSurface<A: AsRawFd + 'static> {
pub(super) dev_id: dev_t, pub(super) dev_id: dev_t,
pub(super) crtc: crtc::Handle, pub(super) crtc: crtc::Handle,
pub(super) plane: plane::Handle, pub(super) primary: plane::Handle,
pub(super) internal: Arc<DrmSurfaceInternal<A>>, pub(super) internal: Arc<DrmSurfaceInternal<A>>,
pub(super) formats: HashSet<Format>, pub(super) has_universal_planes: bool,
#[cfg(feature = "backend_session")] #[cfg(feature = "backend_session")]
pub(super) links: RefCell<Vec<crate::signaling::SignalToken>>, pub(super) links: RefCell<Vec<crate::signaling::SignalToken>>,
} }
@ -48,9 +54,9 @@ impl<A: AsRawFd + 'static> DrmSurface<A> {
self.crtc self.crtc
} }
/// Returns the underlying [`plane`](drm::control::plane) of this surface /// Returns the underlying primary [`plane`](drm::control::plane) of this surface
pub fn plane(&self) -> plane::Handle { pub fn plane(&self) -> plane::Handle {
self.plane self.primary
} }
/// Currently used [`connector`](drm::control::connector)s of this `Surface` /// Currently used [`connector`](drm::control::connector)s of this `Surface`
@ -142,6 +148,35 @@ impl<A: AsRawFd + 'static> DrmSurface<A> {
} }
} }
/// Tries to setup a cursor or overlay [`Plane`](drm::control::plane)
/// to be set at the next commit/page_flip with the given position and size.
///
/// Planes can have arbitrary hardware constraints, that cannot be expressed in the api,
/// like supporting only positions at even or odd values, allowing only certain sizes or disallowing overlapping planes.
/// Using planes should therefor be done in a best-efford manner. Failures on `page_flip` or `commit`
/// should be expected and alternative code paths without the usage of planes prepared.
///
/// Fails if tests for the given plane fail, if the underlying
/// implementation does not support the use of planes or if the plane
/// is not supported by this crtc.
pub fn use_plane(&self, plane: plane::Handle, position: (i32, i32), size: (u32, u32)) -> Result<(), Error> {
match &*self.internal {
DrmSurfaceInternal::Atomic(surf) => surf.use_plane(plane, position, size),
DrmSurfaceInternal::Legacy(_) => Err(Error::NonPrimaryPlane(plane)),
}
}
/// Disables the given plane.
///
/// Errors if the plane is not supported by this crtc or if the underlying
/// implementation does not support the use of planes.
pub fn clear_plane(&self, plane: plane::Handle) -> Result<(), Error> {
match &*self.internal {
DrmSurfaceInternal::Atomic(surf) => surf.clear_plane(plane),
DrmSurfaceInternal::Legacy(_) => Err(Error::NonPrimaryPlane(plane)),
}
}
/// Returns true whenever any state changes are pending to be commited /// Returns true whenever any state changes are pending to be commited
/// ///
/// The following functions may trigger a pending commit: /// The following functions may trigger a pending commit:
@ -165,10 +200,17 @@ impl<A: AsRawFd + 'static> DrmSurface<A> {
/// but will trigger a `vblank` event once done. /// but will trigger a `vblank` event once done.
/// Make sure to [set a `DeviceHandler`](Device::set_handler) and /// Make sure to [set a `DeviceHandler`](Device::set_handler) and
/// [register the belonging `Device`](device_bind) before to receive the event in time. /// [register the belonging `Device`](device_bind) before to receive the event in time.
pub fn commit(&self, framebuffer: framebuffer::Handle, event: bool) -> Result<(), Error> { pub fn commit<'a>(&self, mut framebuffers: impl Iterator<Item=&'a (framebuffer::Handle, plane::Handle)>, event: bool) -> Result<(), Error> {
match &*self.internal { match &*self.internal {
DrmSurfaceInternal::Atomic(surf) => surf.commit(framebuffer, event), DrmSurfaceInternal::Atomic(surf) => surf.commit(framebuffers, event),
DrmSurfaceInternal::Legacy(surf) => surf.commit(framebuffer, event), DrmSurfaceInternal::Legacy(surf) => if let Some((fb, plane)) = framebuffers.next() {
if plane_type(self, *plane)? != PlaneType::Primary {
return Err(Error::NonPrimaryPlane(*plane));
}
surf.commit(*fb, event)
} else {
Ok(())
},
} }
} }
@ -180,16 +222,151 @@ impl<A: AsRawFd + 'static> DrmSurface<A> {
/// This operation is not blocking and will produce a `vblank` event once swapping is done. /// This operation is not blocking and will produce a `vblank` event once swapping is done.
/// Make sure to [set a `DeviceHandler`](Device::set_handler) and /// Make sure to [set a `DeviceHandler`](Device::set_handler) and
/// [register the belonging `Device`](device_bind) before to receive the event in time. /// [register the belonging `Device`](device_bind) before to receive the event in time.
pub fn page_flip(&self, framebuffer: framebuffer::Handle, event: bool) -> Result<(), Error> { pub fn page_flip<'a>(&self, mut framebuffers: impl Iterator<Item=&'a (framebuffer::Handle, plane::Handle)>, event: bool) -> Result<(), Error> {
match &*self.internal { match &*self.internal {
DrmSurfaceInternal::Atomic(surf) => surf.page_flip(framebuffer, event), DrmSurfaceInternal::Atomic(surf) => surf.page_flip(framebuffers, event),
DrmSurfaceInternal::Legacy(surf) => surf.page_flip(framebuffer, event), DrmSurfaceInternal::Legacy(surf) => if let Some((fb, plane)) = framebuffers.next() {
if plane_type(self, *plane)? != PlaneType::Primary {
return Err(Error::NonPrimaryPlane(*plane));
}
surf.page_flip(*fb, event)
} else {
Ok(())
},
} }
} }
/// Returns a set of supported pixel formats for attached buffers /// Returns a set of supported pixel formats for attached buffers
pub fn supported_formats(&self) -> &HashSet<Format> { pub fn supported_formats(&self, plane: plane::Handle) -> Result<HashSet<Format>, Error> {
&self.formats // get plane formats
let plane_info = self.get_plane(plane).map_err(|source| Error::Access {
errmsg: "Error loading plane info",
dev: self.dev_path(),
source,
})?;
let mut formats = HashSet::new();
for code in plane_info
.formats()
.iter()
.flat_map(|x| Fourcc::try_from(*x).ok())
{
formats.insert(Format {
code,
modifier: Modifier::Invalid,
});
}
if let Ok(1) = self.get_driver_capability(DriverCapability::AddFB2Modifiers) {
let set = self.get_properties(plane).map_err(|source| Error::Access {
errmsg: "Failed to query properties",
dev: self.dev_path(),
source,
})?;
let (handles, _) = set.as_props_and_values();
// for every handle ...
let prop = handles
.iter()
.find(|handle| {
// get information of that property
if let Some(info) = self.get_property(**handle).ok() {
// to find out, if we got the handle of the "IN_FORMATS" property ...
if info.name().to_str().map(|x| x == "IN_FORMATS").unwrap_or(false) {
// so we can use that to get formats
return true;
}
}
false
})
.copied();
if let Some(prop) = prop {
let prop_info = self.get_property(prop).map_err(|source| Error::Access {
errmsg: "Failed to query property",
dev: self.dev_path(),
source,
})?;
let (handles, raw_values) = set.as_props_and_values();
let raw_value = raw_values[handles
.iter()
.enumerate()
.find_map(|(i, handle)| if *handle == prop { Some(i) } else { None })
.unwrap()];
if let property::Value::Blob(blob) = prop_info.value_type().convert_value(raw_value) {
let data = self.get_property_blob(blob).map_err(|source| Error::Access {
errmsg: "Failed to query property blob data",
dev: self.dev_path(),
source,
})?;
// be careful here, we have no idea about the alignment inside the blob, so always copy using `read_unaligned`,
// although slice::from_raw_parts would be so much nicer to iterate and to read.
unsafe {
let fmt_mod_blob_ptr = data.as_ptr() as *const drm_ffi::drm_format_modifier_blob;
let fmt_mod_blob = &*fmt_mod_blob_ptr;
let formats_ptr: *const u32 = fmt_mod_blob_ptr
.cast::<u8>()
.offset(fmt_mod_blob.formats_offset as isize)
as *const _;
let modifiers_ptr: *const drm_ffi::drm_format_modifier = fmt_mod_blob_ptr
.cast::<u8>()
.offset(fmt_mod_blob.modifiers_offset as isize)
as *const _;
let formats_ptr = formats_ptr as *const u32;
let modifiers_ptr = modifiers_ptr as *const drm_ffi::drm_format_modifier;
for i in 0..fmt_mod_blob.count_modifiers {
let mod_info = modifiers_ptr.offset(i as isize).read_unaligned();
for j in 0..64 {
if mod_info.formats & (1u64 << j) != 0 {
let code = Fourcc::try_from(
formats_ptr
.offset((j + mod_info.offset) as isize)
.read_unaligned(),
)
.ok();
let modifier = Modifier::from(mod_info.modifier);
if let Some(code) = code {
formats.insert(Format { code, modifier });
}
}
}
}
}
}
}
} else if plane_type(self, plane)? == PlaneType::Cursor {
// Force a LINEAR layout for the cursor if the driver doesn't support modifiers
for format in formats.clone() {
formats.insert(Format {
code: format.code,
modifier: Modifier::Linear,
});
}
}
if formats.is_empty() {
formats.insert(Format {
code: Fourcc::Argb8888,
modifier: Modifier::Invalid,
});
}
let logger = match &*self.internal {
DrmSurfaceInternal::Atomic(surf) => &surf.logger,
DrmSurfaceInternal::Legacy(surf) => &surf.logger,
};
trace!(
logger,
"Supported scan-out formats for plane ({:?}): {:?}",
plane,
formats
);
Ok(formats)
}
/// Returns a set of available planes for this surface
pub fn planes(&self) -> Result<Planes, Error> {
planes(self, &self.crtc, self.has_universal_planes)
} }
/// Tests is a framebuffer can be used with this surface. /// Tests is a framebuffer can be used with this surface.
@ -218,6 +395,32 @@ impl<A: AsRawFd + 'static> DrmSurface<A> {
} }
} }
/// Tests is a framebuffer can be used with this surface and a given plane.
///
/// # Arguments
///
/// - `fb` - Framebuffer handle that has an attached buffer, that shall be tested
/// - `plane` - The plane that should be used to display the buffer
/// (only works for *cursor* and *overlay* planes - for primary planes use `test_buffer`)
/// - `position` - The position of the plane
/// - `size` - The size of the plane
///
/// If the test cannot be performed, this function returns false.
/// This is always the case for non-atomic surfaces.
pub fn test_plane_buffer(
&self,
fb: framebuffer::Handle,
plane: plane::Handle,
position: (i32, i32),
size: (u32, u32),
) -> Result<bool, Error> {
match &*self.internal {
DrmSurfaceInternal::Atomic(surf) => surf.test_plane_buffer(fb, plane, position, size),
DrmSurfaceInternal::Legacy(surf) => { Ok(false) }
// There is no test-commiting with the legacy interface
}
}
/// Re-evaluates the current state of the crtc. /// Re-evaluates the current state of the crtc.
/// ///
/// Usually you do not need to call this, but if the state of /// Usually you do not need to call this, but if the state of