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>>();
|
.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()],
|
||||||
) {
|
) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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`]
|
||||||
///
|
///
|
||||||
|
|
|
@ -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!()
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue