drm: Properly expose planes for surfaces
This commit is contained in:
parent
fd2703a57d
commit
a4f66da69f
|
@ -330,15 +330,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
|||
.collect::<Vec<EncoderInfo>>();
|
||||
'outer: for encoder_info in encoder_infos {
|
||||
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) {
|
||||
info!(
|
||||
logger,
|
||||
|
@ -363,7 +354,6 @@ impl<Data: 'static> UdevHandlerImpl<Data> {
|
|||
};
|
||||
let mut surface = match device.create_surface(
|
||||
crtc,
|
||||
primary,
|
||||
connector_info.modes()[0],
|
||||
&[connector_info.handle()],
|
||||
) {
|
||||
|
|
|
@ -79,13 +79,10 @@ fn main() {
|
|||
// 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.)
|
||||
|
||||
// We just use one plane, the primary one
|
||||
let plane = device.planes(&crtc).unwrap().primary;
|
||||
|
||||
// Initialize the hardware backend
|
||||
let surface = Rc::new(
|
||||
device
|
||||
.create_surface(crtc, plane, mode, &[connector_info.handle()])
|
||||
.create_surface(crtc, mode, &[connector_info.handle()])
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
|
@ -125,7 +122,7 @@ fn main() {
|
|||
.unwrap();
|
||||
|
||||
// Start rendering
|
||||
surface.commit(framebuffer, true).unwrap();
|
||||
surface.commit([(framebuffer, surface.plane())].iter(), true).unwrap();
|
||||
|
||||
// Run
|
||||
event_loop.run(None, &mut (), |_| {}).unwrap();
|
||||
|
@ -161,7 +158,7 @@ impl DeviceHandler for DrmHandlerImpl {
|
|||
}
|
||||
|
||||
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) {
|
||||
|
|
|
@ -181,6 +181,11 @@ impl<A: AsRawFd + 'static> AtomicDrmDevice<A> {
|
|||
dev: self.fd.dev_path(),
|
||||
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)
|
||||
let mut req = AtomicModeReq::new();
|
||||
|
@ -194,6 +199,26 @@ impl<A: AsRawFd + 'static> AtomicDrmDevice<A> {
|
|||
.expect("Unknown property CRTC_ID");
|
||||
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.
|
||||
// Otherwise the commit will not be accepted.
|
||||
for crtc in res_handles.crtcs() {
|
||||
|
|
|
@ -89,6 +89,8 @@ impl<A: AsRawFd + 'static> LegacyDrmDevice<A> {
|
|||
set_connector_state(&*self.fd, res_handles.connectors().iter().copied(), false)?;
|
||||
|
||||
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.)
|
||||
self.fd
|
||||
.set_crtc(*crtc, None, (0, 0), &[], None)
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
|
@ -8,18 +6,17 @@ use std::sync::{atomic::AtomicBool, Arc};
|
|||
|
||||
use calloop::{generic::Generic, InsertError, LoopHandle, Source};
|
||||
use drm::control::{
|
||||
connector, crtc, plane, property, Device as ControlDevice, Event, Mode, PlaneResourceHandles, PlaneType,
|
||||
connector, crtc, Device as ControlDevice, Event, Mode,
|
||||
ResourceHandles,
|
||||
};
|
||||
use drm::{ClientCapability, Device as BasicDevice, DriverCapability};
|
||||
use drm::{ClientCapability, Device as BasicDevice};
|
||||
use nix::libc::dev_t;
|
||||
use nix::sys::stat::fstat;
|
||||
|
||||
pub(super) mod atomic;
|
||||
pub(super) mod legacy;
|
||||
use super::error::Error;
|
||||
use super::{error::Error, Planes, planes};
|
||||
use super::surface::{atomic::AtomicDrmSurface, legacy::LegacyDrmSurface, DrmSurface, DrmSurfaceInternal};
|
||||
use crate::backend::allocator::{Format, Fourcc, Modifier};
|
||||
use atomic::AtomicDrmDevice;
|
||||
use legacy::LegacyDrmDevice;
|
||||
|
||||
|
@ -32,7 +29,6 @@ pub struct DrmDevice<A: AsRawFd + 'static> {
|
|||
pub(super) links: RefCell<Vec<crate::signaling::SignalToken>>,
|
||||
has_universal_planes: bool,
|
||||
resources: ResourceHandles,
|
||||
planes: PlaneResourceHandles,
|
||||
pub(super) logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
|
@ -142,11 +138,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
|
|||
dev: dev.dev_path(),
|
||||
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(
|
||||
dev,
|
||||
active,
|
||||
|
@ -162,7 +153,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
|
|||
links: RefCell::new(Vec::new()),
|
||||
has_universal_planes,
|
||||
resources,
|
||||
planes,
|
||||
logger: log,
|
||||
})
|
||||
}
|
||||
|
@ -258,65 +248,7 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
|
|||
|
||||
/// Returns a set of available planes for a given crtc
|
||||
pub fn planes(&self, crtc: &crtc::Handle) -> Result<Planes, Error> {
|
||||
let mut primary = None;
|
||||
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!()
|
||||
planes(self, crtc, self.has_universal_planes)
|
||||
}
|
||||
|
||||
/// Creates a new rendering surface.
|
||||
|
@ -338,7 +270,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
|
|||
pub fn create_surface(
|
||||
&self,
|
||||
crtc: crtc::Handle,
|
||||
plane: plane::Handle,
|
||||
mode: Mode,
|
||||
connectors: &[connector::Handle],
|
||||
) -> Result<DrmSurface<A>, Error> {
|
||||
|
@ -346,6 +277,7 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
|
|||
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 {
|
||||
errmsg: "Failed to get plane info",
|
||||
dev: self.dev_path(),
|
||||
|
@ -378,10 +310,6 @@ impl<A: AsRawFd + 'static> DrmDevice<A> {
|
|||
self.logger.clone(),
|
||||
)?)
|
||||
} else {
|
||||
if self.plane_type(plane)? != PlaneType::Primary {
|
||||
return Err(Error::NonPrimaryPlane(plane));
|
||||
}
|
||||
|
||||
DrmSurfaceInternal::Legacy(LegacyDrmSurface::new(
|
||||
self.internal.clone(),
|
||||
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 {
|
||||
dev_id: self.dev_id,
|
||||
crtc,
|
||||
plane,
|
||||
primary: plane,
|
||||
internal: Arc::new(internal),
|
||||
formats,
|
||||
has_universal_planes: self.has_universal_planes,
|
||||
#[cfg(feature = "backend_session")]
|
||||
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`]
|
||||
///
|
||||
|
|
|
@ -68,10 +68,100 @@ mod render;
|
|||
pub(self) mod session;
|
||||
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;
|
||||
#[cfg(feature = "backend_gbm")]
|
||||
pub use render::{DrmRenderSurface, Error as DrmRenderError};
|
||||
#[cfg(feature = "backend_session")]
|
||||
pub use session::{DrmDeviceObserver, DrmSurfaceObserver};
|
||||
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!()
|
||||
}
|
|
@ -79,7 +79,7 @@ where
|
|||
|
||||
// select a format
|
||||
let plane_formats = drm
|
||||
.supported_formats()
|
||||
.supported_formats(drm.plane())?
|
||||
.iter()
|
||||
.filter(|fmt| fmt.code == code)
|
||||
.cloned()
|
||||
|
@ -517,9 +517,9 @@ where
|
|||
.fb;
|
||||
|
||||
let flip = if self.drm.commit_pending() {
|
||||
self.drm.commit(fb, true)
|
||||
self.drm.commit([(fb, self.drm.plane())].iter(), true)
|
||||
} else {
|
||||
self.drm.page_flip(fb, true)
|
||||
self.drm.page_flip([(fb, self.drm.plane())].iter(), true)
|
||||
};
|
||||
if flip.is_ok() {
|
||||
self.pending_fb = Some(slot);
|
||||
|
|
|
@ -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(super) fd: Arc<DrmDeviceInternal<A>>,
|
||||
pub(super) active: Arc<AtomicBool>,
|
||||
crtc: crtc::Handle,
|
||||
plane: plane::Handle,
|
||||
additional_planes: Mutex<Vec<PlaneInfo>>,
|
||||
prop_mapping: Mapping,
|
||||
state: RwLock<State>,
|
||||
pending: RwLock<State>,
|
||||
|
@ -147,6 +157,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
active,
|
||||
crtc,
|
||||
plane,
|
||||
additional_planes: Mutex::new(Vec::new()),
|
||||
prop_mapping,
|
||||
state: RwLock::new(state),
|
||||
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.
|
||||
// here we create a dumbbuffer for that purpose.
|
||||
fn create_test_buffer(&self, mode: &Mode) -> Result<framebuffer::Handle, Error> {
|
||||
let (w, h) = mode.size();
|
||||
fn create_test_buffer(&self, size: (u16, u16)) -> Result<framebuffer::Handle, Error> {
|
||||
let (w, h) = size;
|
||||
let db = self
|
||||
.fd
|
||||
.create_dumb_buffer(
|
||||
|
@ -228,7 +239,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
&mut [conn].iter(),
|
||||
&mut [].iter(),
|
||||
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.blob),
|
||||
)?;
|
||||
|
@ -265,7 +277,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
&mut [].iter(),
|
||||
&mut [conn].iter(),
|
||||
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.blob),
|
||||
)?;
|
||||
|
@ -303,7 +316,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
&mut added,
|
||||
&mut removed,
|
||||
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.blob),
|
||||
)?;
|
||||
|
@ -337,12 +351,13 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
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(
|
||||
&mut pending.connectors.iter(),
|
||||
&mut [].iter(),
|
||||
self.plane,
|
||||
test_fb,
|
||||
&[],
|
||||
Some([(test_fb, self.plane)].iter()),
|
||||
Some(mode),
|
||||
Some(new_blob),
|
||||
)?;
|
||||
|
@ -365,11 +380,55 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
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 {
|
||||
*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) {
|
||||
return Err(Error::DeviceInactive);
|
||||
}
|
||||
|
@ -416,7 +475,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
&mut added,
|
||||
&mut removed,
|
||||
self.plane,
|
||||
Some(framebuffer),
|
||||
&*self.additional_planes.lock().unwrap(),
|
||||
Some(framebuffers),
|
||||
Some(pending.mode),
|
||||
Some(pending.blob),
|
||||
)?;
|
||||
|
@ -478,7 +538,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
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) {
|
||||
return Err(Error::DeviceInactive);
|
||||
}
|
||||
|
@ -488,7 +548,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
&mut [].iter(),
|
||||
&mut [].iter(),
|
||||
self.plane,
|
||||
Some(framebuffer),
|
||||
&*self.additional_planes.lock().unwrap(),
|
||||
Some(framebuffers),
|
||||
None,
|
||||
None,
|
||||
)?;
|
||||
|
@ -541,7 +602,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
&mut added,
|
||||
&mut removed,
|
||||
self.plane,
|
||||
Some(fb),
|
||||
&[],
|
||||
Some([(fb, self.plane)].iter()),
|
||||
Some(*mode),
|
||||
Some(blob),
|
||||
)?;
|
||||
|
@ -556,6 +618,38 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
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(
|
||||
&self,
|
||||
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)
|
||||
pub fn build_request(
|
||||
pub fn build_request<'a>(
|
||||
&self,
|
||||
new_connectors: &mut dyn Iterator<Item = &connector::Handle>,
|
||||
removed_connectors: &mut dyn Iterator<Item = &connector::Handle>,
|
||||
plane: plane::Handle,
|
||||
framebuffer: Option<framebuffer::Handle>,
|
||||
primary: plane::Handle,
|
||||
planes: &[PlaneInfo],
|
||||
framebuffers: Option<impl Iterator<Item=&'a (framebuffer::Handle, plane::Handle)>>,
|
||||
mode: Option<Mode>,
|
||||
blob: Option<property::Value<'static>>,
|
||||
) -> Result<AtomicModeReq, Error> {
|
||||
|
@ -674,68 +769,116 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
property::Value::Boolean(true),
|
||||
);
|
||||
|
||||
// and we need to set the framebuffer for our primary plane
|
||||
if let Some(fb) = framebuffer {
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "FB_ID")?,
|
||||
property::Value::Framebuffer(Some(fb)),
|
||||
);
|
||||
// and we need to set the framebuffers for our planes
|
||||
if let Some(fbs) = framebuffers {
|
||||
for (fb, plane) in fbs {
|
||||
req.add_property(
|
||||
*plane,
|
||||
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(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "CRTC_ID")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "CRTC_ID")?,
|
||||
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 {
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "SRC_X")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "SRC_X")?,
|
||||
property::Value::UnsignedRange(0),
|
||||
);
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "SRC_Y")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "SRC_Y")?,
|
||||
property::Value::UnsignedRange(0),
|
||||
);
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "SRC_W")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "SRC_W")?,
|
||||
// these are 16.16. fixed point
|
||||
property::Value::UnsignedRange((mode.size().0 as u64) << 16),
|
||||
);
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "SRC_H")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "SRC_H")?,
|
||||
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.
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "CRTC_X")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "CRTC_X")?,
|
||||
property::Value::SignedRange(0),
|
||||
);
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "CRTC_Y")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "CRTC_Y")?,
|
||||
property::Value::SignedRange(0),
|
||||
);
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "CRTC_W")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "CRTC_W")?,
|
||||
property::Value::UnsignedRange(mode.size().0 as u64),
|
||||
);
|
||||
req.add_property(
|
||||
plane,
|
||||
self.plane_prop_handle(plane, "CRTC_H")?,
|
||||
primary,
|
||||
self.plane_prop_handle(primary, "CRTC_H")?,
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -743,7 +886,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
// this is mostly used to remove the contents quickly, e.g. on tty switch,
|
||||
// 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.
|
||||
pub fn clear_plane(&self) -> Result<(), Error> {
|
||||
pub fn clear_plane(&self, plane: plane::Handle) -> Result<(), Error> {
|
||||
let mut req = AtomicModeReq::new();
|
||||
|
||||
req.add_property(
|
||||
|
@ -758,17 +901,19 @@ impl<A: AsRawFd + 'static> AtomicDrmSurface<A> {
|
|||
property::Value::Framebuffer(None),
|
||||
);
|
||||
|
||||
self.fd
|
||||
.atomic_commit(&[AtomicCommitFlags::TestOnly], req.clone())
|
||||
.map_err(|_| Error::TestFailed(self.crtc))?;
|
||||
|
||||
self.fd
|
||||
let result = self.fd
|
||||
.atomic_commit(&[AtomicCommitFlags::Nonblock], req)
|
||||
.map_err(|source| Error::Access {
|
||||
errmsg: "Failed to commit on clear_plane",
|
||||
dev: self.fd.dev_path(),
|
||||
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>(
|
||||
|
@ -801,8 +946,13 @@ impl<A: AsRawFd + 'static> Drop for AtomicDrmSurface<A> {
|
|||
|
||||
// other ttys that use no cursor, might not clear it themselves.
|
||||
// This makes sure our cursor won't stay visible.
|
||||
if let Err(err) = self.clear_plane() {
|
||||
warn!(self.logger, "Failed to clear plane on {:?}: {}", self.crtc, err);
|
||||
if let Err(err) = self.clear_plane(self.plane) {
|
||||
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
|
||||
|
|
|
@ -1,17 +1,23 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashSet;
|
||||
use std::convert::TryFrom;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use drm::control::{connector, crtc, framebuffer, plane, Device as ControlDevice, Mode};
|
||||
use drm::Device as BasicDevice;
|
||||
use drm::control::{connector, crtc, framebuffer, plane, property, Device as ControlDevice, Mode};
|
||||
use drm::{Device as BasicDevice, DriverCapability};
|
||||
|
||||
use nix::libc::dev_t;
|
||||
|
||||
pub(super) mod atomic;
|
||||
pub(super) mod legacy;
|
||||
use super::error::Error;
|
||||
use crate::backend::allocator::Format;
|
||||
use super::{
|
||||
error::Error,
|
||||
Planes, PlaneType,
|
||||
plane_type, planes,
|
||||
device::DevPath,
|
||||
};
|
||||
use crate::backend::allocator::{Format, Fourcc, Modifier};
|
||||
use atomic::AtomicDrmSurface;
|
||||
use legacy::LegacyDrmSurface;
|
||||
|
||||
|
@ -19,9 +25,9 @@ use legacy::LegacyDrmSurface;
|
|||
pub struct DrmSurface<A: AsRawFd + 'static> {
|
||||
pub(super) dev_id: dev_t,
|
||||
pub(super) crtc: crtc::Handle,
|
||||
pub(super) plane: plane::Handle,
|
||||
pub(super) primary: plane::Handle,
|
||||
pub(super) internal: Arc<DrmSurfaceInternal<A>>,
|
||||
pub(super) formats: HashSet<Format>,
|
||||
pub(super) has_universal_planes: bool,
|
||||
#[cfg(feature = "backend_session")]
|
||||
pub(super) links: RefCell<Vec<crate::signaling::SignalToken>>,
|
||||
}
|
||||
|
@ -48,9 +54,9 @@ impl<A: AsRawFd + 'static> DrmSurface<A> {
|
|||
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 {
|
||||
self.plane
|
||||
self.primary
|
||||
}
|
||||
|
||||
/// 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
|
||||
///
|
||||
/// 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.
|
||||
/// Make sure to [set a `DeviceHandler`](Device::set_handler) and
|
||||
/// [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 {
|
||||
DrmSurfaceInternal::Atomic(surf) => surf.commit(framebuffer, event),
|
||||
DrmSurfaceInternal::Legacy(surf) => surf.commit(framebuffer, event),
|
||||
DrmSurfaceInternal::Atomic(surf) => surf.commit(framebuffers, 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.
|
||||
/// Make sure to [set a `DeviceHandler`](Device::set_handler) and
|
||||
/// [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 {
|
||||
DrmSurfaceInternal::Atomic(surf) => surf.page_flip(framebuffer, event),
|
||||
DrmSurfaceInternal::Legacy(surf) => surf.page_flip(framebuffer, event),
|
||||
DrmSurfaceInternal::Atomic(surf) => surf.page_flip(framebuffers, 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
|
||||
pub fn supported_formats(&self) -> &HashSet<Format> {
|
||||
&self.formats
|
||||
pub fn supported_formats(&self, plane: plane::Handle) -> Result<HashSet<Format>, Error> {
|
||||
// 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.
|
||||
|
@ -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.
|
||||
///
|
||||
/// Usually you do not need to call this, but if the state of
|
||||
|
|
Loading…
Reference in New Issue