From 9acd109a042f054362118a1751cb6c60309f04e0 Mon Sep 17 00:00:00 2001 From: Victor Brekenfeld Date: Fri, 22 May 2020 23:14:49 +0200 Subject: [PATCH] fallback: support FallbackDevice for automatic nvidia support --- src/backend/drm/common/fallback.rs | 343 +++++++++++++++++++++++------ src/backend/drm/egl/mod.rs | 2 +- 2 files changed, 280 insertions(+), 65 deletions(-) diff --git a/src/backend/drm/common/fallback.rs b/src/backend/drm/common/fallback.rs index 6a47b31..597864b 100644 --- a/src/backend/drm/common/fallback.rs +++ b/src/backend/drm/common/fallback.rs @@ -2,9 +2,20 @@ //! Types to make fallback device initialization easier //! +#[cfg(feature = "backend_drm_egl")] +use crate::backend::drm::egl::{Arguments as EglDeviceArguments, EglDevice, Error as EglDeviceError}; #[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))] use crate::backend::drm::{atomic::AtomicDrmDevice, legacy::LegacyDrmDevice}; use crate::backend::drm::{common::Error, Device, DeviceHandler, RawDevice, RawSurface, Surface}; +#[cfg(all(feature = "backend_drm_gbm", feature = "backend_drm_eglstream"))] +use crate::backend::drm::{ + eglstream::{EglStreamDevice, Error as EglStreamError}, + gbm::{Error as GbmError, GbmDevice}, +}; +#[cfg(feature = "backend_drm_egl")] +use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements}; +#[cfg(feature = "backend_drm_egl")] +use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface}; use crate::backend::egl::Error as EGLError; #[cfg(feature = "use_system_lib")] use crate::backend::egl::{display::EGLBufferReader, EGLGraphicsBackend}; @@ -36,23 +47,25 @@ pub enum FallbackDevice { Fallback(D2), } -struct FallbackDeviceHandlerD1( +struct FallbackDeviceHandlerD1( Box> + 'static>, ) where - E: std::error::Error + Send + 'static, + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: Surface + 'static, - S2: Surface + 'static, + S1: Surface + 'static, + S2: Surface + 'static, D1: Device + 'static, D2: Device + 'static; -impl DeviceHandler for FallbackDeviceHandlerD1 +impl DeviceHandler for FallbackDeviceHandlerD1 where - E: std::error::Error + Send + 'static, + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: Surface + 'static, - S2: Surface + 'static, + S1: Surface + 'static, + S2: Surface + 'static, D1: Device + 'static, D2: Device + 'static, { @@ -61,28 +74,30 @@ where fn vblank(&mut self, crtc: crtc::Handle) { self.0.vblank(crtc) } - fn error(&mut self, error: E) { - self.0.error(error); + fn error(&mut self, error: E1) { + self.0.error(EitherError::Either(error)); } } -struct FallbackDeviceHandlerD2( +struct FallbackDeviceHandlerD2( Box> + 'static>, ) where - E: std::error::Error + Send + 'static, + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: Surface + 'static, - S2: Surface + 'static, + S1: Surface + 'static, + S2: Surface + 'static, D1: Device + 'static, D2: Device + 'static; -impl DeviceHandler for FallbackDeviceHandlerD2 +impl DeviceHandler for FallbackDeviceHandlerD2 where - E: std::error::Error + Send + 'static, + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: Surface + 'static, - S2: Surface + 'static, + S1: Surface + 'static, + S2: Surface + 'static, D1: Device + 'static, D2: Device + 'static, { @@ -91,8 +106,8 @@ where fn vblank(&mut self, crtc: crtc::Handle) { self.0.vblank(crtc) } - fn error(&mut self, error: E) { - self.0.error(error); + fn error(&mut self, error: E2) { + self.0.error(EitherError::Or(error)); } } @@ -147,9 +162,33 @@ pub enum FallbackSurface { Fallback(S2), } +/// Enum uniting two kinds of possible errors. +#[derive(Debug, thiserror::Error)] +pub enum EitherError { + /// Either this error + #[error("{0}")] + Either(#[source] E1), + /// Or this error + #[error("{0}")] + Or(#[source] E2), +} + +impl Into for EitherError +where + E1: std::error::Error + Into + 'static, + E2: std::error::Error + Into + 'static, +{ + fn into(self) -> SwapBuffersError { + match self { + EitherError::Either(err) => err.into(), + EitherError::Or(err) => err.into(), + } + } +} + #[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))] impl FallbackDevice, LegacyDrmDevice> { - /// Try to initialize an [`AtomicDrmDevice`](::backend::drm:;atomic::AtomicDrmDevice) + /// Try to initialize an [`AtomicDrmDevice`](::backend::drm::atomic::AtomicDrmDevice) /// and fall back to a [`LegacyDrmDevice`] if atomic-modesetting is not supported. /// /// # Arguments @@ -203,6 +242,146 @@ impl FallbackDevice, LegacyDrmD } } +#[cfg(all( + feature = "backend_drm_gbm", + feature = "backend_drm_eglstream", + feature = "backend_udev" +))] +type GbmOrEglStreamError = EitherError< + GbmError<<::Surface as Surface>::Error>, + EglStreamError<<::Surface as Surface>::Error>, +>; +#[cfg(all( + feature = "backend_drm_gbm", + feature = "backend_drm_eglstream", + feature = "backend_udev" +))] +impl FallbackDevice, EglStreamDevice> +where + D: RawDevice + ControlDevice + 'static, +{ + /// Try to initialize a [`GbmDevice`](::backend::drm::gbm::GbmDevice) + /// or a [`EglStreamDevice`](::backend::drm::eglstream::EglStreamDevice) depending on the used driver. + /// + /// # Arguments + /// + /// - `dev` - Open drm device (needs implement [`RawDevice`](::backend::drm::RawDevice)) + /// - `logger` - Optional [`slog::Logger`] to be used by the resulting device. + /// + /// # Return + /// + /// Returns an error, if the choosen device fails to initialize. + pub fn new(dev: D, logger: L) -> Result> + where + L: Into>, + { + let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm_fallback")); + + let driver = crate::backend::udev::driver(dev.device_id()).expect("Failed to query device"); + info!(log, "Drm device driver: {:?}", driver); + if driver.as_ref().and_then(|x| x.to_str()) == Some("nvidia") { + Ok(FallbackDevice::Fallback( + EglStreamDevice::new(dev, log).map_err(EitherError::Or)?, + )) + } else { + Ok(FallbackDevice::Preference( + GbmDevice::new(dev, log.clone()).map_err(EitherError::Either)?, + )) + } + } +} + +#[cfg(feature = "backend_drm_egl")] +type EglUnderlying = EitherError< + EglDeviceError<<::Surface as Surface>::Error>, + EglDeviceError<<::Surface as Surface>::Error>, +>; + +#[cfg(feature = "backend_drm_egl")] +type FallbackEglDevice = FallbackDevice, EglDevice>; + +#[cfg(feature = "backend_drm_egl")] +impl FallbackDevice +where + D1: Device + 'static, + ::Surface: NativeSurface::Surface as Surface>::Error>, + D2: Device + 'static, + ::Surface: NativeSurface::Surface as Surface>::Error>, +{ + /// Try to create a new [`EglDevice`] from a [`FallbackDevice`] containing two compatible device types. + /// + /// This helper function is necessary as implementing [`NativeDevice`](::backend::egl::native::NativeDevice) for [`FallbackDevice`] is impossible + /// as selecting the appropriate [`Backend`](::backend::egl::native::Backend) would be impossible without knowing + /// the underlying device type, that was selected by [`FallbackDevice`]. + /// + /// Returns an error if the context creation was not successful. + pub fn new_egl( + dev: FallbackDevice, + logger: L, + ) -> Result, EglUnderlying> + where + B1: Backend::Surface, Error = <::Surface as Surface>::Error> + + 'static, + D1: NativeDisplay, + B2: Backend::Surface, Error = <::Surface as Surface>::Error> + + 'static, + D2: NativeDisplay, + L: Into>, + { + let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm_fallback")); + match dev { + FallbackDevice::Preference(gbm) => match EglDevice::new(gbm, log) { + Ok(dev) => Ok(FallbackDevice::Preference(dev)), + Err(err) => Err(EglUnderlying::::Either(err)), + }, + FallbackDevice::Fallback(eglstream) => match EglDevice::new(eglstream, log) { + Ok(dev) => Ok(FallbackDevice::Fallback(dev)), + Err(err) => Err(EglUnderlying::::Or(err)), + }, + } + } + + /// Try to create a new [`EglDevice`] from a [`FallbackDevice`] containing two compatible device types with + /// the given attributes and requirements as defaults for new surfaces. + /// + /// This helper function is necessary as implementing [`NativeDevice`](::backend::egl::native::NativeDevice) for [`FallbackDevice`] is impossible + /// as selecting the appropriate [`Backend`](::backend::egl::native::Backend) would be impossible without knowing + /// the underlying device type, that was selected by [`FallbackDevice`]. + /// + /// Returns an error if the context creation was not successful. + pub fn new_egl_with_defaults( + dev: FallbackDevice, + default_attributes: GlAttributes, + default_requirements: PixelFormatRequirements, + logger: L, + ) -> Result, EglUnderlying> + where + B1: Backend::Surface, Error = <::Surface as Surface>::Error> + + 'static, + D1: NativeDisplay, + B2: Backend::Surface, Error = <::Surface as Surface>::Error> + + 'static, + D2: NativeDisplay, + L: Into>, + { + let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_drm_fallback")); + match dev { + FallbackDevice::Preference(gbm) => { + match EglDevice::new_with_defaults(gbm, default_attributes, default_requirements, log) { + Ok(dev) => Ok(FallbackDevice::Preference(dev)), + Err(err) => Err(EglUnderlying::::Either(err)), + } + } + FallbackDevice::Fallback(eglstream) => { + match EglDevice::new_with_defaults(eglstream, default_attributes, default_requirements, log) { + Ok(dev) => Ok(FallbackDevice::Fallback(dev)), + Err(err) => Err(EglUnderlying::::Or(err)), + } + } + } + } +} + macro_rules! fallback_device_impl { ($func_name:ident, $self:ty, $return:ty, $($arg_name:ident : $arg_ty:ty),*) => { fn $func_name(self: $self, $($arg_name : $arg_ty),*) -> $return { @@ -219,6 +398,19 @@ macro_rules! fallback_device_impl { fallback_device_impl!($func_name, $self, ()); }; } +macro_rules! fallback_device_err_impl { + ($func_name:ident, $self:ty, $return:ty, $($arg_name:ident : $arg_ty:ty),*) => { + fn $func_name(self: $self, $($arg_name : $arg_ty),*) -> $return { + match self { + FallbackDevice::Preference(dev) => dev.$func_name($($arg_name),*).map_err(EitherError::Either), + FallbackDevice::Fallback(dev) => dev.$func_name($($arg_name),*).map_err(EitherError::Or), + } + } + }; + ($func_name:ident, $self:ty, $return:ty) => { + fallback_device_err_impl!($func_name, $self, $return,); + }; +} macro_rules! fallback_surface_impl { ($func_name:ident, $self:ty, $return:ty, $($arg_name:ident : $arg_ty:ty),*) => { @@ -236,6 +428,16 @@ macro_rules! fallback_surface_impl { fallback_surface_impl!($func_name, $self, ()); }; } +macro_rules! fallback_surface_err_impl { + ($func_name:ident, $self:ty, $return:ty, $($arg_name:ident : $arg_ty:ty),*) => { + fn $func_name(self: $self, $($arg_name : $arg_ty),*) -> $return { + match self { + FallbackSurface::Preference(dev) => dev.$func_name($($arg_name),*).map_err(EitherError::Either), + FallbackSurface::Fallback(dev) => dev.$func_name($($arg_name),*).map_err(EitherError::Or), + } + } + }; +} impl AsRawFd for FallbackDevice { fallback_device_impl!(as_raw_fd, &Self, RawFd); @@ -243,13 +445,14 @@ impl AsRawFd for FallbackDevice { impl BasicDevice for FallbackDevice {} impl ControlDevice for FallbackDevice {} -impl Device for FallbackDevice +impl Device for FallbackDevice where - // Connectors and Error need to match for both Surfaces - E: std::error::Error + Send + 'static, + // Connectors need to match for both Surfaces + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: Surface + 'static, - S2: Surface + 'static, + S1: Surface + 'static, + S2: Surface + 'static, D1: Device + 'static, D2: Device + 'static, { @@ -268,18 +471,20 @@ where crtc: crtc::Handle, mode: Mode, connectors: &[connector::Handle], - ) -> Result { + ) -> Result> { match self { FallbackDevice::Preference(dev) => Ok(FallbackSurface::Preference( - dev.create_surface(crtc, mode, connectors)?, + dev.create_surface(crtc, mode, connectors) + .map_err(EitherError::Either)?, )), FallbackDevice::Fallback(dev) => Ok(FallbackSurface::Fallback( - dev.create_surface(crtc, mode, connectors)?, + dev.create_surface(crtc, mode, connectors) + .map_err(EitherError::Or)?, )), } } fallback_device_impl!(process_events, &mut Self); - fallback_device_impl!(resource_handles, &Self, Result); + fallback_device_err_impl!(resource_handles, &Self, Result>); fallback_device_impl!(get_connector_info, &Self, Result, conn: connector::Handle); fallback_device_impl!(get_crtc_info, &Self, Result, crtc: crtc::Handle); fallback_device_impl!(get_encoder_info, &Self, Result, enc: encoder::Handle); @@ -288,13 +493,14 @@ where } // Impl RawDevice where underlying types implement RawDevice -impl RawDevice for FallbackDevice +impl RawDevice for FallbackDevice where - // Connectors and Error need to match for both Surfaces - E: std::error::Error + Send + 'static, + // Connectors need to match for both Surfaces + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: RawSurface + Surface + 'static, - S2: RawSurface + Surface + 'static, + S1: RawSurface + Surface + 'static, + S2: RawSurface + Surface + 'static, D1: RawDevice + 'static, D2: RawDevice + 'static, { @@ -308,41 +514,47 @@ impl, display : &Display); } -impl Surface for FallbackSurface +impl Surface for FallbackSurface where - // Connectors and Error need to match for both Surfaces - E: std::error::Error + Send + 'static, + // Connectors need to match for both Surfaces + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: Surface + 'static, - S2: Surface + 'static, + S1: Surface + 'static, + S2: Surface + 'static, { - type Error = E; + type Error = EitherError; type Connectors = C; fallback_surface_impl!(crtc, &Self, crtc::Handle); fallback_surface_impl!(current_connectors, &Self, C); fallback_surface_impl!(pending_connectors, &Self, C); - fallback_surface_impl!(add_connector, &Self, Result<(), E>, conn: connector::Handle); - fallback_surface_impl!(remove_connector, &Self, Result<(), E>, conn: connector::Handle); - fallback_surface_impl!(set_connectors, &Self, Result<(), E>, conns: &[connector::Handle]); + fallback_surface_err_impl!(add_connector, &Self, Result<(), EitherError>, conn: connector::Handle); + fallback_surface_err_impl!(remove_connector, &Self, Result<(), EitherError>, conn: connector::Handle); + fallback_surface_err_impl!(set_connectors, &Self, Result<(), EitherError>, conns: &[connector::Handle]); fallback_surface_impl!(current_mode, &Self, Mode); fallback_surface_impl!(pending_mode, &Self, Mode); - fallback_surface_impl!(use_mode, &Self, Result<(), E>, mode: Mode); + fallback_surface_err_impl!(use_mode, &Self, Result<(), EitherError>, mode: Mode); } -impl RawSurface for FallbackSurface +impl RawSurface for FallbackSurface where - E: std::error::Error + Send + 'static, + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: RawSurface + Surface + 'static, - S2: RawSurface + Surface + 'static, + S1: RawSurface + Surface + 'static, + S2: RawSurface + Surface + 'static, { fallback_surface_impl!(commit_pending, &Self, bool); - fallback_surface_impl!(commit, &Self, Result<(), E>, fb: framebuffer::Handle); - fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), E> { + fallback_surface_err_impl!(commit, &Self, Result<(), EitherError>, fb: framebuffer::Handle); + fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), EitherError> { match self { - FallbackSurface::Preference(dev) => RawSurface::page_flip(dev, framebuffer), - FallbackSurface::Fallback(dev) => RawSurface::page_flip(dev, framebuffer), + FallbackSurface::Preference(dev) => { + RawSurface::page_flip(dev, framebuffer).map_err(EitherError::Either) + } + FallbackSurface::Fallback(dev) => { + RawSurface::page_flip(dev, framebuffer).map_err(EitherError::Or) + } } } } @@ -353,29 +565,32 @@ impl AsRawFd for FallbackSurface BasicDevice for FallbackSurface {} impl ControlDevice for FallbackSurface {} -impl CursorBackend for FallbackSurface +impl CursorBackend for FallbackSurface where E1: std::error::Error + Send + 'static, - E2: 'static, + E2: std::error::Error + Send + 'static, + E3: std::error::Error + 'static, + E4: std::error::Error + 'static, CF: ?Sized, C: IntoIterator + 'static, - S1: Surface + CursorBackend + 'static, - S2: Surface + CursorBackend + 'static, + S1: Surface + CursorBackend + 'static, + S2: Surface + CursorBackend + 'static, { type CursorFormat = CF; - type Error = E2; + type Error = EitherError; - fallback_surface_impl!(set_cursor_position, &Self, Result<(), E2>, x: u32, y: u32); - fallback_surface_impl!(set_cursor_representation, &Self, Result<(), E2>, buffer: &Self::CursorFormat, hotspot: (u32, u32)); + fallback_surface_err_impl!(set_cursor_position, &Self, Result<(), EitherError>, x: u32, y: u32); + fallback_surface_err_impl!(set_cursor_representation, &Self, Result<(), EitherError>, buffer: &Self::CursorFormat, hotspot: (u32, u32)); } #[cfg(feature = "renderer_gl")] -impl GLGraphicsBackend for FallbackSurface +impl GLGraphicsBackend for FallbackSurface where - E: std::error::Error + Send + 'static, + E1: std::error::Error + Send + 'static, + E2: std::error::Error + Send + 'static, C: IntoIterator + 'static, - S1: Surface + GLGraphicsBackend + 'static, - S2: Surface + GLGraphicsBackend + 'static, + S1: Surface + GLGraphicsBackend + 'static, + S2: Surface + GLGraphicsBackend + 'static, { fallback_surface_impl!(swap_buffers, &Self, Result<(), SwapBuffersError>); fallback_surface_impl!(get_proc_address, &Self, *const c_void, symbol: &str); diff --git a/src/backend/drm/egl/mod.rs b/src/backend/drm/egl/mod.rs index 2a19e56..70f9934 100644 --- a/src/backend/drm/egl/mod.rs +++ b/src/backend/drm/egl/mod.rs @@ -46,7 +46,7 @@ pub enum Error); +pub(crate) type Arguments = (crtc::Handle, Mode, Vec); type BackendRef = Weak::Surface>>; /// Representation of an egl device to create egl rendering surfaces