diff --git a/src/backend/drm/surface/atomic.rs b/src/backend/drm/surface/atomic.rs index 47b7f0f..67d989f 100644 --- a/src/backend/drm/surface/atomic.rs +++ b/src/backend/drm/surface/atomic.rs @@ -23,14 +23,14 @@ pub struct State { pub struct AtomicDrmSurface { pub(super) fd: Arc>, - active: Arc, + pub(super) active: Arc, crtc: crtc::Handle, plane: plane::Handle, prop_mapping: Mapping, state: RwLock, pending: RwLock, test_buffer: Mutex>, - logger: ::slog::Logger, + pub(super) logger: ::slog::Logger, } impl AtomicDrmSurface { @@ -484,6 +484,37 @@ impl AtomicDrmSurface { Ok(()) } + + pub fn test_buffer(&self, fb: framebuffer::Handle, mode: &Mode) -> Result { + if !self.active.load(Ordering::SeqCst) { + return Err(Error::DeviceInactive); + } + + let blob = self.fd + .create_property_blob(&mode) + .map_err(|source| Error::Access { + errmsg: "Failed to create Property Blob for mode", + dev: self.fd.dev_path(), + source, + })?; + + let req = self.build_request( + &mut [].iter(), + &mut [].iter(), + self.plane, + Some(fb), + Some(*mode), + Some(blob) + )?; + + let result = self.fd.atomic_commit(&[AtomicCommitFlags::AllowModeset, AtomicCommitFlags::TestOnly], req).is_ok(); + + if let Err(err) = self.fd.destroy_framebuffer(fb) { + debug!(self.logger, "Failed to destroy framebuffer({:?}): {}", fb, err); + } + + Ok(result) + } pub(crate) fn conn_prop_handle( &self, diff --git a/src/backend/drm/surface/legacy.rs b/src/backend/drm/surface/legacy.rs index 7aaa59b..40e7799 100644 --- a/src/backend/drm/surface/legacy.rs +++ b/src/backend/drm/surface/legacy.rs @@ -21,11 +21,11 @@ pub struct State { pub struct LegacyDrmSurface { pub(super) fd: Arc>, - active: Arc, + pub(super) active: Arc, crtc: crtc::Handle, state: RwLock, pending: RwLock, - logger: ::slog::Logger, + pub(super) logger: ::slog::Logger, } impl LegacyDrmSurface { @@ -312,6 +312,21 @@ impl LegacyDrmSurface { }) } + pub fn test_buffer(&self, fb: framebuffer::Handle, mode: &Mode) -> Result { + if !self.active.load(Ordering::SeqCst) { + return Err(Error::DeviceInactive); + } + + debug!(self.logger, "Setting screen for buffer *testing*"); + Ok(self.fd.set_crtc( + self.crtc, + Some(fb), + (0, 0), + &[], + Some(*mode), + ).is_ok()) + } + // we use this function to verify, if a certain connector/mode combination // is valid on our crtc. We do this with the most basic information we have: // - is there a matching encoder diff --git a/src/backend/drm/surface/mod.rs b/src/backend/drm/surface/mod.rs index 58e290c..6c2a90b 100644 --- a/src/backend/drm/surface/mod.rs +++ b/src/backend/drm/surface/mod.rs @@ -7,10 +7,10 @@ use drm::control::{Device as ControlDevice, Mode, crtc, connector, framebuffer, pub(super) mod atomic; pub(super) mod legacy; -use super::error::Error; +use super::{error::Error, device::DevPath}; use atomic::AtomicDrmSurface; use legacy::LegacyDrmSurface; -use crate::backend::allocator::Format; +use crate::backend::allocator::{Format, Fourcc, Modifier}; pub struct DrmSurface { @@ -184,4 +184,65 @@ impl DrmSurface { pub fn supported_formats(&self) -> &HashSet { &self.formats } + + pub fn test_buffer(&self, buffer: &B, bpp: u32, modifier: Option<[Modifier; 4]>, mode: &Mode, allow_screen_change: bool) -> Result, Error> + where + B: drm::buffer::Buffer + drm::buffer::PlanarBuffer + { + use std::sync::atomic::Ordering; + + let (active, logger) = match &*self.internal { + DrmSurfaceInternal::Atomic(surf) => (surf.active.load(Ordering::SeqCst), surf.logger.clone()), + DrmSurfaceInternal::Legacy(surf) => (surf.active.load(Ordering::SeqCst), surf.logger.clone()), + }; + + if !active { + return Err(Error::DeviceInactive); + } + + let fb = match + if let Some(mods) = modifier { + self.add_planar_framebuffer(buffer, unsafe { + std::mem::transmute::<&[Modifier; 4], &[Option; 4]>(&mods) + }, drm_ffi::DRM_MODE_FB_MODIFIERS) + } else { + self.add_planar_framebuffer(buffer, &[None, None, None, None], 0) + } + { + Ok(fb) => fb, + Err(_) => { + // We only support this as a fallback of last resort for ARGB8888 visuals, + // like xf86-video-modesetting does. + if drm::buffer::Buffer::format(buffer) != Fourcc::Argb8888 + || buffer.handles()[1].is_some() { + return Ok(false); + } + debug!(logger, "Failed to add framebuffer, trying legacy method"); + self.add_framebuffer(buffer, bpp, 32).map_err(|source| Error::Access { + errmsg: "Failed to add framebuffer", + dev: self.dev_path(), + source, + })? + } + }; + + let result = match &*self.internal { + DrmSurfaceInternal::Atomic(surf) => surf.test_buffer(fb, mode), + DrmSurfaceInternal::Legacy(surf) => if allow_screen_change { + surf.test_buffer(fb, mode) + } else { Ok(false) } // There is no test-commiting with the legacy interface + }; + + if !result.unwrap_or(false) { + if let Err(err) = self.destroy_framebuffer(fb) { + debug!(logger, "Failed to destroy framebuffer({:?}): {}", fb, err); + } + } + + result.map(|x| if x { + Some(fb) + } else { + None + }) + } } \ No newline at end of file