drm: add set_connectors to Surface

This commit is contained in:
Victor Brekenfeld 2020-04-18 18:43:08 +02:00
parent d3b8563f65
commit ea44343315
7 changed files with 218 additions and 131 deletions

View File

@ -49,7 +49,9 @@ impl<A: AsRawFd + 'static> SessionObserver for AtomicDrmDeviceObserver<A> {
return; return;
} }
} }
// TODO: Clear overlay planes (if we ever use them) // TODO: Clear overlay planes (if we ever use them)
if let Some(backends) = self.backends.upgrade() { if let Some(backends) = self.backends.upgrade() {
for surface in backends.borrow().values().filter_map(Weak::upgrade) { for surface in backends.borrow().values().filter_map(Weak::upgrade) {
// other ttys that use no cursor, might not clear it themselves. // other ttys that use no cursor, might not clear it themselves.

View File

@ -181,11 +181,15 @@ impl<A: AsRawFd + 'static> Surface for AtomicDrmSurfaceInternal<A> {
// check if the connector can handle the current mode // check if the connector can handle the current mode
if info.modes().contains(pending.mode.as_ref().unwrap()) { if info.modes().contains(pending.mode.as_ref().unwrap()) {
let mut conns = pending.connectors.clone();
conns.insert(conn);
// check if config is supported // check if config is supported
let req = self.build_request(&conns, &self.planes, None, pending.mode, pending.blob)?; let req = self.build_request(
&mut [conn].iter(),
&mut [].iter(),
&self.planes,
None,
pending.mode,
pending.blob,
)?;
self.atomic_commit( self.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly], &[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req, req,
@ -194,7 +198,7 @@ impl<A: AsRawFd + 'static> Surface for AtomicDrmSurfaceInternal<A> {
.map_err(|_| Error::TestFailed(self.crtc))?; .map_err(|_| Error::TestFailed(self.crtc))?;
// seems to be, lets add the connector // seems to be, lets add the connector
pending.connectors = conns; pending.connectors.insert(conn);
Ok(()) Ok(())
} else { } else {
@ -202,15 +206,18 @@ impl<A: AsRawFd + 'static> Surface for AtomicDrmSurfaceInternal<A> {
} }
} }
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> { fn remove_connector(&self, conn: connector::Handle) -> Result<(), Error> {
let mut pending = self.pending.write().unwrap(); let mut pending = self.pending.write().unwrap();
// remove it temporary
let mut conns = pending.connectors.clone();
conns.remove(&connector);
// check if new config is supported (should be) // check if new config is supported (should be)
let req = self.build_request(&conns, &self.planes, None, pending.mode, pending.blob)?; let req = self.build_request(
&mut [].iter(),
&mut [conn].iter(),
&self.planes,
None,
pending.mode,
pending.blob,
)?;
self.atomic_commit( self.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly], &[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req, req,
@ -219,37 +226,37 @@ impl<A: AsRawFd + 'static> Surface for AtomicDrmSurfaceInternal<A> {
.map_err(|_| Error::TestFailed(self.crtc))?; .map_err(|_| Error::TestFailed(self.crtc))?;
// seems to be, lets remove the connector // seems to be, lets remove the connector
pending.connectors.remove(&conn);
Ok(())
}
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> {
let current = self.state.write().unwrap();
let mut pending = self.pending.write().unwrap();
let conns = connectors.iter().cloned().collect::<HashSet<_>>();
let mut added = conns.difference(&current.connectors);
let mut removed = current.connectors.difference(&conns);
let req = self.build_request(
&mut added,
&mut removed,
&self.planes,
None,
pending.mode,
pending.blob,
)?;
self.atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
req,
)
.map_err(|_| Error::TestFailed(self.crtc))?;
pending.connectors = conns; pending.connectors = conns;
// try to disable it Ok(())
let mut req = AtomicModeReq::new();
req.add_property(
connector,
self.conn_prop_handle(connector, "CRTC_ID")?,
property::Value::CRTC(None),
);
if let Err(err) = self
.atomic_commit(&[AtomicCommitFlags::TestOnly], req.clone())
.compat()
.map_err(|_| Error::TestFailed(self.crtc))
{
warn!(
self.logger,
"Could not disable connector ({:?}) (but rendering will be stopped): {}", connector, err
);
Ok(())
} else {
// should succeed, any error is serious
self.atomic_commit(&[AtomicCommitFlags::Nonblock], req.clone())
.compat()
.map_err(|source| Error::Access {
errmsg: "Failed to commit disable connector",
dev: self.dev_path(),
source,
})
}
} }
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> { fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> {
@ -269,7 +276,14 @@ impl<A: AsRawFd + 'static> Surface for AtomicDrmSurfaceInternal<A> {
None => property::Value::Unknown(0), None => property::Value::Unknown(0),
}); });
let req = self.build_request(&pending.connectors, &self.planes, None, mode, new_blob)?; let req = self.build_request(
&mut pending.connectors.iter(),
&mut [].iter(),
&self.planes,
None,
mode,
new_blob,
)?;
if let Err(err) = self if let Err(err) = self
.atomic_commit( .atomic_commit(
&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly], &[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly],
@ -304,41 +318,41 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
"Preparing Commit.\n\tCurrent: {:?}\n\tPending: {:?}\n", *current, *pending "Preparing Commit.\n\tCurrent: {:?}\n\tPending: {:?}\n", *current, *pending
); );
{ let current_conns = current.connectors.clone();
let current_conns = current.connectors.clone(); let pending_conns = pending.connectors.clone();
let pending_conns = pending.connectors.clone(); let mut removed = current_conns.difference(&pending_conns);
let removed = current_conns.difference(&pending_conns); let mut added = pending_conns.difference(&current_conns);
let added = pending_conns.difference(&current_conns);
for conn in removed { for conn in removed.clone() {
if let Ok(info) = self.get_connector(*conn) { if let Ok(info) = self.get_connector(*conn) {
info!(self.logger, "Removing connector: {:?}", info.interface()); info!(self.logger, "Removing connector: {:?}", info.interface());
} else { } else {
info!(self.logger, "Removing unknown connector"); info!(self.logger, "Removing unknown connector");
}
}
for conn in added {
if let Ok(info) = self.get_connector(*conn) {
info!(self.logger, "Adding connector: {:?}", info.interface());
} else {
info!(self.logger, "Adding unknown connector");
}
}
if current.mode != pending.mode {
info!(
self.logger,
"Setting new mode: {:?}",
pending.mode.as_ref().unwrap().name()
);
} }
} }
for conn in added.clone() {
if let Ok(info) = self.get_connector(*conn) {
info!(self.logger, "Adding connector: {:?}", info.interface());
} else {
info!(self.logger, "Adding unknown connector");
}
}
if current.mode != pending.mode {
info!(
self.logger,
"Setting new mode: {:?}",
pending.mode.as_ref().unwrap().name()
);
}
trace!(self.logger, "Testing screen config"); trace!(self.logger, "Testing screen config");
{
let req = {
let req = self.build_request( let req = self.build_request(
&pending.connectors, &mut added,
&mut removed,
&self.planes, &self.planes,
Some(framebuffer), Some(framebuffer),
pending.mode, pending.mode,
@ -360,6 +374,15 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
info!(self.logger, "Reverting back to last know good state"); info!(self.logger, "Reverting back to last know good state");
*pending = current.clone(); *pending = current.clone();
self.build_request(
&mut [].iter(),
&mut [].iter(),
&self.planes,
Some(framebuffer),
current.mode,
current.blob,
)?
} else { } else {
if current.mode != pending.mode { if current.mode != pending.mode {
if let Some(blob) = current.blob { if let Some(blob) = current.blob {
@ -369,16 +392,12 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
} }
} }
*current = pending.clone(); *current = pending.clone();
}
}
let req = self.build_request( // new config
&current.connectors, req
&self.planes, }
Some(framebuffer), };
current.mode,
current.blob,
)?;
debug!(self.logger, "Setting screen: {:#?}", req); debug!(self.logger, "Setting screen: {:#?}", req);
self.atomic_commit( self.atomic_commit(
&[ &[
@ -399,9 +418,15 @@ impl<A: AsRawFd + 'static> RawSurface for AtomicDrmSurfaceInternal<A> {
} }
fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), SwapBuffersError> { fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), SwapBuffersError> {
let current = self.state.read().unwrap();
let req = self let req = self
.build_request(&current.connectors, &self.planes, Some(framebuffer), None, None) //current.mode) .build_request(
&mut [].iter(),
&mut [].iter(),
&self.planes,
Some(framebuffer),
None,
None,
) //current.mode)
.map_err(|_| SwapBuffersError::ContextLost)?; .map_err(|_| SwapBuffersError::ContextLost)?;
trace!(self.logger, "Queueing page flip: {:#?}", req); trace!(self.logger, "Queueing page flip: {:#?}", req);
self.atomic_commit( self.atomic_commit(
@ -530,7 +555,8 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<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)
fn build_request( fn build_request(
&self, &self,
connectors: &HashSet<connector::Handle>, new_connectors: &mut dyn Iterator<Item = &connector::Handle>,
removed_connectors: &mut dyn Iterator<Item = &connector::Handle>,
planes: &Planes, planes: &Planes,
framebuffer: Option<framebuffer::Handle>, framebuffer: Option<framebuffer::Handle>,
mode: Option<Mode>, mode: Option<Mode>,
@ -538,7 +564,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
) -> Result<AtomicModeReq, Error> { ) -> Result<AtomicModeReq, Error> {
let mut req = AtomicModeReq::new(); let mut req = AtomicModeReq::new();
for conn in connectors.iter() { for conn in new_connectors {
req.add_property( req.add_property(
*conn, *conn,
self.conn_prop_handle(*conn, "CRTC_ID")?, self.conn_prop_handle(*conn, "CRTC_ID")?,
@ -546,6 +572,14 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
); );
} }
for conn in removed_connectors {
req.add_property(
*conn,
self.conn_prop_handle(*conn, "CRTC_ID")?,
property::Value::CRTC(None),
);
}
if let Some(blob) = blob { if let Some(blob) = blob {
req.add_property(self.crtc, self.crtc_prop_handle(self.crtc, "MODE_ID")?, blob); req.add_property(self.crtc, self.crtc_prop_handle(self.crtc, "MODE_ID")?, blob);
} }
@ -816,6 +850,10 @@ impl<A: AsRawFd + 'static> Surface for AtomicDrmSurface<A> {
self.0.remove_connector(connector) self.0.remove_connector(connector)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Error> {
self.0.set_connectors(connectors)
}
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> { fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> {
self.0.use_mode(mode) self.0.use_mode(mode)
} }

View File

@ -49,6 +49,10 @@ where
.map_err(Error::Underlying) .map_err(Error::Underlying)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.surface.set_connectors(connectors).map_err(Error::Underlying)
}
fn current_mode(&self) -> Option<Mode> { fn current_mode(&self) -> Option<Mode> {
self.surface.current_mode() self.surface.current_mode()
} }

View File

@ -163,6 +163,10 @@ impl<D: RawDevice + 'static> Surface for GbmSurfaceInternal<D> {
self.crtc.remove_connector(connector).map_err(Error::Underlying) self.crtc.remove_connector(connector).map_err(Error::Underlying)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.crtc.set_connectors(connectors).map_err(Error::Underlying)
}
fn current_mode(&self) -> Option<Mode> { fn current_mode(&self) -> Option<Mode> {
self.crtc.current_mode() self.crtc.current_mode()
} }
@ -317,6 +321,10 @@ impl<D: RawDevice + 'static> Surface for GbmSurface<D> {
self.0.remove_connector(connector) self.0.remove_connector(connector)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.0.set_connectors(connectors)
}
fn current_mode(&self) -> Option<Mode> { fn current_mode(&self) -> Option<Mode> {
self.0.current_mode() self.0.current_mode()
} }

View File

@ -233,6 +233,9 @@ impl<A: AsRawFd + 'static> Device for LegacyDrmDevice<A> {
} }
} }
// Now try to do the least possible amount of changes to set this to the state the users requested
// TODO!
let state = State { mode, connectors }; let state = State { mode, connectors };
let backend = Rc::new(LegacyDrmSurfaceInternal { let backend = Rc::new(LegacyDrmSurfaceInternal {
dev: self.dev.clone(), dev: self.dev.clone(),

View File

@ -105,64 +105,34 @@ impl<A: AsRawFd + 'static> Surface for LegacyDrmSurfaceInternal<A> {
} }
fn add_connector(&self, conn: connector::Handle) -> Result<(), Error> { fn add_connector(&self, conn: connector::Handle) -> Result<(), Error> {
let info = self
.get_connector(conn)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading connector info",
dev: self.dev_path(),
source,
})?;
let mut pending = self.pending.write().unwrap(); let mut pending = self.pending.write().unwrap();
// check if the connector can handle the current mode if self.check_connector(conn, pending.mode.as_ref().unwrap())? {
if info.modes().contains(pending.mode.as_ref().unwrap()) {
// check if there is a valid encoder
let encoders = info
.encoders()
.iter()
.filter(|enc| enc.is_some())
.map(|enc| enc.unwrap())
.map(|encoder| {
self.get_encoder(encoder)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading encoder info",
dev: self.dev_path(),
source,
})
})
.collect::<Result<Vec<encoder::Info>, _>>()?;
// and if any encoder supports the selected crtc
let resource_handles = self.resource_handles().compat().map_err(|source| Error::Access {
errmsg: "Error loading resources",
dev: self.dev_path(),
source,
})?;
if !encoders
.iter()
.map(|encoder| encoder.possible_crtcs())
.all(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&self.crtc))
{
return Err(Error::NoSuitableEncoder {
connector: info.handle(),
crtc: self.crtc,
});
}
pending.connectors.insert(conn); pending.connectors.insert(conn);
Ok(())
} else {
Err(Error::ModeNotSuitable(pending.mode.unwrap()))
} }
Ok(())
} }
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> { fn remove_connector(&self, connector: connector::Handle) -> Result<(), Error> {
self.pending.write().unwrap().connectors.remove(&connector); self.pending.write().unwrap().connectors.remove(&connector);
Ok(()) Ok(())
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
let mut pending = self.pending.write().unwrap();
if connectors
.iter()
.map(|conn| self.check_connector(*conn, pending.mode.as_ref().unwrap()))
.collect::<Result<Vec<bool>, _>>()?
.iter().all(|v| *v)
{
pending.connectors = connectors.into_iter().cloned().collect();
}
Ok(())
}
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> { fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> {
let mut pending = self.pending.write().unwrap(); let mut pending = self.pending.write().unwrap();
@ -279,6 +249,57 @@ impl<A: AsRawFd + 'static> RawSurface for LegacyDrmSurfaceInternal<A> {
} }
} }
impl<A: AsRawFd + 'static> LegacyDrmSurfaceInternal<A> {
fn check_connector(&self, conn: connector::Handle, mode: &Mode) -> Result<bool, Error> {
let info = self
.get_connector(conn)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading connector info",
dev: self.dev_path(),
source,
})?;
// check if the connector can handle the current mode
if info.modes().contains(mode) {
// check if there is a valid encoder
let encoders = info
.encoders()
.iter()
.filter(|enc| enc.is_some())
.map(|enc| enc.unwrap())
.map(|encoder| {
self.get_encoder(encoder)
.compat()
.map_err(|source| Error::Access {
errmsg: "Error loading encoder info",
dev: self.dev_path(),
source,
})
})
.collect::<Result<Vec<encoder::Info>, _>>()?;
// and if any encoder supports the selected crtc
let resource_handles = self.resource_handles().compat().map_err(|source| Error::Access {
errmsg: "Error loading resources",
dev: self.dev_path(),
source,
})?;
if !encoders
.iter()
.map(|encoder| encoder.possible_crtcs())
.all(|crtc_list| resource_handles.filter_crtcs(crtc_list).contains(&self.crtc))
{
Ok(false)
} else {
Ok(true)
}
} else {
Ok(false)
}
}
}
impl<A: AsRawFd + 'static> Drop for LegacyDrmSurfaceInternal<A> { impl<A: AsRawFd + 'static> Drop for LegacyDrmSurfaceInternal<A> {
fn drop(&mut self) { fn drop(&mut self) {
// ignore failure at this point // ignore failure at this point
@ -347,6 +368,10 @@ impl<A: AsRawFd + 'static> Surface for LegacyDrmSurface<A> {
self.0.remove_connector(connector) self.0.remove_connector(connector)
} }
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
self.0.set_connectors(connectors)
}
fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> { fn use_mode(&self, mode: Option<Mode>) -> Result<(), Error> {
self.0.use_mode(mode) self.0.use_mode(mode)
} }

View File

@ -172,6 +172,13 @@ pub trait Surface {
/// Tries to mark a [`connector`](drm::control::connector) /// Tries to mark a [`connector`](drm::control::connector)
/// for removal on the next commit. /// for removal on the next commit.
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>; fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error>;
/// Tries to replace the current connector set with the newly provided one on the next commit.
///
/// Fails if one new `connector` is not compatible with the underlying [`crtc`](drm::control::crtc)
/// (e.g. no suitable [`encoder`](drm::control::encoder) may be found)
/// or is not compatible with the currently pending
/// [`Mode`](drm::control::Mode).
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error>;
/// Returns the currently active [`Mode`](drm::control::Mode) /// Returns the currently active [`Mode`](drm::control::Mode)
/// of the underlying [`crtc`](drm::control::crtc) /// of the underlying [`crtc`](drm::control::crtc)
/// if any. /// if any.