Merge pull request #214 from Smithay/feature/nvidia
Add support for nvidia's EGLStream API
This commit is contained in:
commit
2d5e829e12
|
@ -19,6 +19,7 @@ jobs:
|
|||
- backend_drm
|
||||
- backend_drm_legacy
|
||||
- backend_drm_gbm
|
||||
- backend_drm_eglstream
|
||||
- backend_drm_egl
|
||||
- backend_libinput
|
||||
- backend_udev
|
||||
|
|
|
@ -45,12 +45,13 @@ slog-term = "2.3"
|
|||
gl_generator = { version = "0.14", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["backend_winit", "backend_drm_legacy", "backend_drm_atomic", "backend_drm_gbm", "backend_drm_egl", "backend_libinput", "backend_udev", "backend_session_logind", "renderer_glium", "xwayland", "wayland_frontend"]
|
||||
default = ["backend_winit", "backend_drm_legacy", "backend_drm_atomic", "backend_drm_gbm", "backend_drm_eglstream", "backend_drm_egl", "backend_libinput", "backend_udev", "backend_session_logind", "renderer_glium", "xwayland", "wayland_frontend"]
|
||||
backend_winit = ["winit", "wayland-server/dlopen", "backend_egl", "wayland-egl", "renderer_gl", "use_system_lib"]
|
||||
backend_drm = ["drm", "failure"]
|
||||
backend_drm_atomic = ["backend_drm"]
|
||||
backend_drm_legacy = ["backend_drm"]
|
||||
backend_drm_gbm = ["backend_drm", "gbm", "image"]
|
||||
backend_drm_eglstream = ["backend_drm", "backend_egl"]
|
||||
backend_drm_egl = ["backend_drm", "backend_egl"]
|
||||
backend_egl = ["gl_generator"]
|
||||
backend_libinput = ["input"]
|
||||
|
|
|
@ -29,5 +29,5 @@ gl_generator = "0.14"
|
|||
default = [ "winit", "egl", "udev", "logind" ]
|
||||
egl = [ "smithay/use_system_lib" ]
|
||||
winit = [ "smithay/backend_winit" ]
|
||||
udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm_atomic", "smithay/backend_drm_legacy", "smithay/backend_drm_gbm", "smithay/backend_drm_egl", "smithay/backend_session", "input" ]
|
||||
udev = [ "smithay/backend_libinput", "smithay/backend_udev", "smithay/backend_drm_atomic", "smithay/backend_drm_legacy", "smithay/backend_drm_gbm", "smithay/backend_drm_eglstream", "smithay/backend_drm_egl", "smithay/backend_session", "input" ]
|
||||
logind = [ "smithay/backend_session_logind" ]
|
||||
|
|
|
@ -18,9 +18,10 @@ use smithay::{
|
|||
backend::{
|
||||
drm::{
|
||||
atomic::AtomicDrmDevice,
|
||||
common::fallback::FallbackDevice,
|
||||
common::fallback::{FallbackDevice, FallbackSurface},
|
||||
device_bind,
|
||||
egl::{EglDevice, EglSurface},
|
||||
eglstream::{egl::EglStreamDeviceBackend, EglStreamDevice, EglStreamSurface},
|
||||
gbm::{egl::Gbm as EglGbmBackend, GbmDevice, GbmSurface},
|
||||
legacy::LegacyDrmDevice,
|
||||
DevPath, Device, DeviceHandler, Surface,
|
||||
|
@ -76,12 +77,20 @@ impl AsRawFd for SessionFd {
|
|||
}
|
||||
}
|
||||
|
||||
type RenderDevice = EglDevice<
|
||||
EglGbmBackend<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>,
|
||||
GbmDevice<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>,
|
||||
type RenderDevice = FallbackDevice<
|
||||
EglDevice<
|
||||
EglGbmBackend<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>,
|
||||
GbmDevice<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>,
|
||||
>,
|
||||
EglDevice<
|
||||
EglStreamDeviceBackend<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>,
|
||||
EglStreamDevice<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>,
|
||||
>,
|
||||
>;
|
||||
type RenderSurface = FallbackSurface<
|
||||
EglSurface<GbmSurface<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>>,
|
||||
EglSurface<EglStreamSurface<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>>,
|
||||
>;
|
||||
type RenderSurface =
|
||||
EglSurface<GbmSurface<FallbackDevice<AtomicDrmDevice<SessionFd>, LegacyDrmDevice<SessionFd>>>>;
|
||||
|
||||
pub fn run_udev(
|
||||
display: Rc<RefCell<Display>>,
|
||||
|
@ -301,7 +310,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
|
|||
.filter_map(|e| *e)
|
||||
.flat_map(|encoder_handle| device.get_encoder_info(encoder_handle))
|
||||
.collect::<Vec<EncoderInfo>>();
|
||||
for encoder_info in encoder_infos {
|
||||
'outer: for encoder_info in encoder_infos {
|
||||
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
||||
if let Entry::Vacant(entry) = backends.entry(crtc) {
|
||||
let renderer = GliumDrawer::init(
|
||||
|
@ -313,7 +322,7 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
|
|||
);
|
||||
|
||||
entry.insert(Rc::new(renderer));
|
||||
break;
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -349,14 +358,14 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
|
|||
.filter_map(|e| *e)
|
||||
.flat_map(|encoder_handle| device.get_encoder_info(encoder_handle))
|
||||
.collect::<Vec<EncoderInfo>>();
|
||||
for encoder_info in encoder_infos {
|
||||
'outer: for encoder_info in encoder_infos {
|
||||
for crtc in res_handles.filter_crtcs(encoder_info.possible_crtcs()) {
|
||||
if !backends.contains_key(&crtc) {
|
||||
let renderer =
|
||||
GliumDrawer::init(device.create_surface(crtc).unwrap(), logger.clone());
|
||||
|
||||
backends.insert(crtc, Rc::new(renderer));
|
||||
break;
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -376,23 +385,29 @@ impl<S: SessionNotifier, Data: 'static> UdevHandlerImpl<S, Data> {
|
|||
OFlag::O_RDWR | OFlag::O_CLOEXEC | OFlag::O_NOCTTY | OFlag::O_NONBLOCK,
|
||||
)
|
||||
.ok()
|
||||
.and_then(
|
||||
|fd| match FallbackDevice::new(SessionFd(fd), true, self.logger.clone()) {
|
||||
.and_then(|fd| {
|
||||
match FallbackDevice::<AtomicDrmDevice<_>, LegacyDrmDevice<_>>::new(
|
||||
SessionFd(fd),
|
||||
true,
|
||||
self.logger.clone(),
|
||||
) {
|
||||
Ok(drm) => Some(drm),
|
||||
Err(err) => {
|
||||
warn!(self.logger, "Skipping drm device, because of error: {}", err);
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.and_then(|drm| match GbmDevice::new(drm, self.logger.clone()) {
|
||||
Ok(gbm) => Some(gbm),
|
||||
Err(err) => {
|
||||
warn!(self.logger, "Skipping gbm device, because of error: {}", err);
|
||||
None
|
||||
}
|
||||
})
|
||||
.and_then(|gbm| match EglDevice::new(gbm, self.logger.clone()) {
|
||||
.and_then(|drm| {
|
||||
match FallbackDevice::<GbmDevice<_>, EglStreamDevice<_>>::new(drm, self.logger.clone()) {
|
||||
Ok(dev) => Some(dev),
|
||||
Err(err) => {
|
||||
warn!(self.logger, "Skipping device, because of error: {}", err);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.and_then(|dev| match FallbackDevice::new_egl(dev, self.logger.clone()) {
|
||||
Ok(egl) => Some(egl),
|
||||
Err(err) => {
|
||||
warn!(self.logger, "Skipping egl device, because of error: {}", err);
|
||||
|
@ -657,6 +672,7 @@ impl DrmRenderer {
|
|||
};
|
||||
|
||||
if reschedule {
|
||||
debug!(self.logger, "Rescheduling");
|
||||
match (timer, evt_handle) {
|
||||
(Some(handle), _) => {
|
||||
let _ = handle.add_timeout(
|
||||
|
|
11
build.rs
11
build.rs
|
@ -32,6 +32,17 @@ fn main() {
|
|||
"EGL_EXT_platform_wayland",
|
||||
"EGL_EXT_platform_device",
|
||||
"EGL_KHR_image_base",
|
||||
"EGL_EXT_output_base",
|
||||
"EGL_EXT_output_drm",
|
||||
"EGL_EXT_device_drm",
|
||||
"EGL_EXT_device_enumeration",
|
||||
"EGL_EXT_device_query",
|
||||
"EGL_KHR_stream",
|
||||
"EGL_KHR_stream_producer_eglsurface",
|
||||
"EGL_EXT_stream_consumer_egloutput",
|
||||
"EGL_KHR_stream_fifo",
|
||||
"EGL_NV_output_drm_flip_event",
|
||||
"EGL_NV_stream_attrib",
|
||||
],
|
||||
)
|
||||
.write_bindings(gl_generator::GlobalGenerator, &mut file)
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
#![warn(rust_2018_idioms)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate slog;
|
||||
|
||||
use glium::Surface as GliumSurface;
|
||||
use slog::Drain;
|
||||
use smithay::{
|
||||
backend::{
|
||||
drm::{
|
||||
atomic::AtomicDrmDevice,
|
||||
common::fallback::{EitherError, FallbackDevice},
|
||||
common::Error as DrmError,
|
||||
device_bind,
|
||||
egl::{EglDevice, EglSurface, Error as EglError},
|
||||
eglstream::{
|
||||
egl::EglStreamDeviceBackend, EglStreamDevice, EglStreamSurface, Error as EglStreamError,
|
||||
},
|
||||
legacy::LegacyDrmDevice,
|
||||
Device, DeviceHandler,
|
||||
},
|
||||
graphics::glium::GliumGraphicsBackend,
|
||||
},
|
||||
reexports::{
|
||||
calloop::EventLoop,
|
||||
drm::control::{connector::State as ConnectorState, crtc},
|
||||
},
|
||||
};
|
||||
use std::{
|
||||
fs::{File, OpenOptions},
|
||||
io::Error as IoError,
|
||||
os::unix::io::{AsRawFd, RawFd},
|
||||
rc::Rc,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
pub struct ClonableFile {
|
||||
file: File,
|
||||
}
|
||||
|
||||
impl AsRawFd for ClonableFile {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ClonableFile {
|
||||
fn clone(&self) -> Self {
|
||||
ClonableFile {
|
||||
file: self.file.try_clone().expect("Unable to clone file"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let log = slog::Logger::root(Mutex::new(slog_term::term_full().fuse()).fuse(), o!());
|
||||
|
||||
/*
|
||||
* Initialize the drm backend
|
||||
*/
|
||||
|
||||
// "Find" a suitable drm device
|
||||
let mut options = OpenOptions::new();
|
||||
options.read(true);
|
||||
options.write(true);
|
||||
let file = ClonableFile {
|
||||
file: options.open("/dev/dri/card1").expect("Failed to open card1"),
|
||||
};
|
||||
let mut device = EglDevice::new(
|
||||
EglStreamDevice::new(
|
||||
FallbackDevice::<AtomicDrmDevice<_>, LegacyDrmDevice<_>>::new(file, true, log.clone())
|
||||
.expect("Failed to initialize drm device"),
|
||||
log.clone(),
|
||||
)
|
||||
.expect("Failed to initialize egl stream device"),
|
||||
log.clone(),
|
||||
)
|
||||
.expect("Failed to initialize egl device");
|
||||
|
||||
// Get a set of all modesetting resource handles (excluding planes):
|
||||
let res_handles = Device::resource_handles(&device).unwrap();
|
||||
|
||||
// Use first connected connector
|
||||
let connector_info = res_handles
|
||||
.connectors()
|
||||
.iter()
|
||||
.map(|conn| Device::get_connector_info(&device, *conn).unwrap())
|
||||
.find(|conn| conn.state() == ConnectorState::Connected)
|
||||
.unwrap();
|
||||
println!("Conn: {:?}", connector_info.interface());
|
||||
|
||||
// Use the first encoder
|
||||
let encoder_info =
|
||||
Device::get_encoder_info(&device, connector_info.encoders()[0].expect("expected encoder")).unwrap();
|
||||
|
||||
// use the connected crtc if any
|
||||
let crtc = encoder_info
|
||||
.crtc()
|
||||
// or use the first one that is compatible with the encoder
|
||||
.unwrap_or_else(|| {
|
||||
*res_handles
|
||||
.filter_crtcs(encoder_info.possible_crtcs())
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap()
|
||||
});
|
||||
println!("Crtc {:?}", crtc);
|
||||
|
||||
// Assuming we found a good connector and loaded the info into `connector_info`
|
||||
let mode = connector_info.modes()[0]; // Use first mode (usually highest resolution, but in reality you should filter and sort and check and match with other connectors, if you use more then one.)
|
||||
println!("Mode: {:?}", mode);
|
||||
|
||||
// Initialize the hardware backend
|
||||
let surface = device
|
||||
.create_surface(crtc, mode, &[connector_info.handle()])
|
||||
.expect("Failed to create surface");
|
||||
|
||||
let backend: Rc<GliumGraphicsBackend<_>> = Rc::new(surface.into());
|
||||
device.set_handler(DrmHandlerImpl {
|
||||
surface: backend.clone(),
|
||||
});
|
||||
|
||||
/*
|
||||
* Register the DrmDevice on the EventLoop
|
||||
*/
|
||||
let mut event_loop = EventLoop::<()>::new().unwrap();
|
||||
let _source = device_bind(&event_loop.handle(), device)
|
||||
.map_err(|err| -> IoError { err.into() })
|
||||
.unwrap();
|
||||
|
||||
// Start rendering
|
||||
{
|
||||
if let Err(smithay::backend::graphics::SwapBuffersError::ContextLost(err)) = backend.draw().finish() {
|
||||
println!("{}", err);
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
// Run
|
||||
event_loop.run(None, &mut (), |_| {}).unwrap();
|
||||
}
|
||||
|
||||
pub struct DrmHandlerImpl {
|
||||
surface: Rc<
|
||||
GliumGraphicsBackend<
|
||||
EglSurface<
|
||||
EglStreamSurface<
|
||||
FallbackDevice<AtomicDrmDevice<ClonableFile>, LegacyDrmDevice<ClonableFile>>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl DeviceHandler for DrmHandlerImpl {
|
||||
type Device = EglDevice<
|
||||
EglStreamDeviceBackend<FallbackDevice<AtomicDrmDevice<ClonableFile>, LegacyDrmDevice<ClonableFile>>>,
|
||||
EglStreamDevice<FallbackDevice<AtomicDrmDevice<ClonableFile>, LegacyDrmDevice<ClonableFile>>>,
|
||||
>;
|
||||
|
||||
fn vblank(&mut self, _crtc: crtc::Handle) {
|
||||
{
|
||||
println!("Vblank");
|
||||
let mut frame = self.surface.draw();
|
||||
frame.clear(None, Some((0.8, 0.8, 0.9, 1.0)), false, Some(1.0), None);
|
||||
if let Err(smithay::backend::graphics::SwapBuffersError::ContextLost(err)) = frame.finish() {
|
||||
panic!("Failed to swap: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&mut self, error: EglError<EglStreamError<EitherError<DrmError, DrmError>>>) {
|
||||
panic!("{:?}", error);
|
||||
}
|
||||
}
|
|
@ -61,7 +61,7 @@ type Mapping = (
|
|||
HashMap<plane::Handle, HashMap<String, property::Handle>>,
|
||||
);
|
||||
|
||||
struct Dev<A: AsRawFd + 'static> {
|
||||
pub(in crate::backend::drm) struct Dev<A: AsRawFd + 'static> {
|
||||
fd: A,
|
||||
privileged: bool,
|
||||
active: Arc<AtomicBool>,
|
||||
|
|
|
@ -38,11 +38,11 @@ pub struct Planes {
|
|||
pub cursor: plane::Handle,
|
||||
}
|
||||
|
||||
pub(super) struct AtomicDrmSurfaceInternal<A: AsRawFd + 'static> {
|
||||
pub(in crate::backend::drm) struct AtomicDrmSurfaceInternal<A: AsRawFd + 'static> {
|
||||
pub(super) dev: Rc<Dev<A>>,
|
||||
pub(super) crtc: crtc::Handle,
|
||||
pub(in crate::backend::drm) crtc: crtc::Handle,
|
||||
pub(super) cursor: CursorState,
|
||||
pub(super) planes: Planes,
|
||||
pub(in crate::backend::drm) planes: Planes,
|
||||
pub(super) state: RwLock<State>,
|
||||
pub(super) pending: RwLock<State>,
|
||||
pub(super) logger: ::slog::Logger,
|
||||
|
@ -918,7 +918,7 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
|
|||
))
|
||||
}
|
||||
|
||||
pub(crate) fn clear_plane(&self, plane: plane::Handle) -> Result<(), Error> {
|
||||
pub(super) fn clear_plane(&self, plane: plane::Handle) -> Result<(), Error> {
|
||||
let mut req = AtomicModeReq::new();
|
||||
|
||||
req.add_property(
|
||||
|
@ -948,7 +948,9 @@ impl<A: AsRawFd + 'static> AtomicDrmSurfaceInternal<A> {
|
|||
}
|
||||
|
||||
/// Open raw crtc utilizing atomic mode-setting
|
||||
pub struct AtomicDrmSurface<A: AsRawFd + 'static>(pub(super) Rc<AtomicDrmSurfaceInternal<A>>);
|
||||
pub struct AtomicDrmSurface<A: AsRawFd + 'static>(
|
||||
pub(in crate::backend::drm) Rc<AtomicDrmSurfaceInternal<A>>,
|
||||
);
|
||||
|
||||
impl<A: AsRawFd + 'static> AsRawFd for AtomicDrmSurface<A> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
|
|
|
@ -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<D1: Device + 'static, D2: Device + 'static> {
|
|||
Fallback(D2),
|
||||
}
|
||||
|
||||
struct FallbackDeviceHandlerD1<E, C, S1, S2, D1, D2>(
|
||||
struct FallbackDeviceHandlerD1<E1, E2, C, S1, S2, D1, D2>(
|
||||
Box<dyn DeviceHandler<Device = FallbackDevice<D1, D2>> + 'static>,
|
||||
)
|
||||
where
|
||||
E: std::error::Error + Send + 'static,
|
||||
E1: std::error::Error + Send + 'static,
|
||||
E2: std::error::Error + Send + 'static,
|
||||
C: IntoIterator<Item = connector::Handle> + 'static,
|
||||
S1: Surface<Error = E, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E, Connectors = C> + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E2, Connectors = C> + 'static,
|
||||
D1: Device<Surface = S1> + 'static,
|
||||
D2: Device<Surface = S2> + 'static;
|
||||
|
||||
impl<E, C, S1, S2, D1, D2> DeviceHandler for FallbackDeviceHandlerD1<E, C, S1, S2, D1, D2>
|
||||
impl<E1, E2, C, S1, S2, D1, D2> DeviceHandler for FallbackDeviceHandlerD1<E1, E2, C, S1, S2, D1, D2>
|
||||
where
|
||||
E: std::error::Error + Send + 'static,
|
||||
E1: std::error::Error + Send + 'static,
|
||||
E2: std::error::Error + Send + 'static,
|
||||
C: IntoIterator<Item = connector::Handle> + 'static,
|
||||
S1: Surface<Error = E, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E, Connectors = C> + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E2, Connectors = C> + 'static,
|
||||
D1: Device<Surface = S1> + 'static,
|
||||
D2: Device<Surface = S2> + '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<E, C, S1, S2, D1, D2>(
|
||||
struct FallbackDeviceHandlerD2<E1, E2, C, S1, S2, D1, D2>(
|
||||
Box<dyn DeviceHandler<Device = FallbackDevice<D1, D2>> + 'static>,
|
||||
)
|
||||
where
|
||||
E: std::error::Error + Send + 'static,
|
||||
E1: std::error::Error + Send + 'static,
|
||||
E2: std::error::Error + Send + 'static,
|
||||
C: IntoIterator<Item = connector::Handle> + 'static,
|
||||
S1: Surface<Error = E, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E, Connectors = C> + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E2, Connectors = C> + 'static,
|
||||
D1: Device<Surface = S1> + 'static,
|
||||
D2: Device<Surface = S2> + 'static;
|
||||
|
||||
impl<E, C, S1, S2, D1, D2> DeviceHandler for FallbackDeviceHandlerD2<E, C, S1, S2, D1, D2>
|
||||
impl<E1, E2, C, S1, S2, D1, D2> DeviceHandler for FallbackDeviceHandlerD2<E1, E2, C, S1, S2, D1, D2>
|
||||
where
|
||||
E: std::error::Error + Send + 'static,
|
||||
E1: std::error::Error + Send + 'static,
|
||||
E2: std::error::Error + Send + 'static,
|
||||
C: IntoIterator<Item = connector::Handle> + 'static,
|
||||
S1: Surface<Error = E, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E, Connectors = C> + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E2, Connectors = C> + 'static,
|
||||
D1: Device<Surface = S1> + 'static,
|
||||
D2: Device<Surface = S2> + '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<S1: Surface, S2: Surface> {
|
|||
Fallback(S2),
|
||||
}
|
||||
|
||||
/// Enum uniting two kinds of possible errors.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum EitherError<E1: std::error::Error + 'static, E2: std::error::Error + 'static> {
|
||||
/// Either this error
|
||||
#[error("{0}")]
|
||||
Either(#[source] E1),
|
||||
/// Or this error
|
||||
#[error("{0}")]
|
||||
Or(#[source] E2),
|
||||
}
|
||||
|
||||
impl<E1, E2> Into<SwapBuffersError> for EitherError<E1, E2>
|
||||
where
|
||||
E1: std::error::Error + Into<SwapBuffersError> + 'static,
|
||||
E2: std::error::Error + Into<SwapBuffersError> + '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<A: AsRawFd + Clone + 'static> FallbackDevice<AtomicDrmDevice<A>, LegacyDrmDevice<A>> {
|
||||
/// 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<A: AsRawFd + Clone + 'static> FallbackDevice<AtomicDrmDevice<A>, LegacyDrmD
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(all(
|
||||
feature = "backend_drm_gbm",
|
||||
feature = "backend_drm_eglstream",
|
||||
feature = "backend_udev"
|
||||
))]
|
||||
type GbmOrEglStreamError<D> = EitherError<
|
||||
GbmError<<<D as Device>::Surface as Surface>::Error>,
|
||||
EglStreamError<<<D as Device>::Surface as Surface>::Error>,
|
||||
>;
|
||||
#[cfg(all(
|
||||
feature = "backend_drm_gbm",
|
||||
feature = "backend_drm_eglstream",
|
||||
feature = "backend_udev"
|
||||
))]
|
||||
impl<D> FallbackDevice<GbmDevice<D>, EglStreamDevice<D>>
|
||||
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<L>(dev: D, logger: L) -> Result<Self, GbmOrEglStreamError<D>>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
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<D1, D2> = EitherError<
|
||||
EglDeviceError<<<D1 as Device>::Surface as Surface>::Error>,
|
||||
EglDeviceError<<<D2 as Device>::Surface as Surface>::Error>,
|
||||
>;
|
||||
|
||||
#[cfg(feature = "backend_drm_egl")]
|
||||
type FallbackEglDevice<B1, D1, B2, D2> = FallbackDevice<EglDevice<B1, D1>, EglDevice<B2, D2>>;
|
||||
|
||||
#[cfg(feature = "backend_drm_egl")]
|
||||
impl<D1, D2> FallbackDevice<D1, D2>
|
||||
where
|
||||
D1: Device + 'static,
|
||||
<D1 as Device>::Surface: NativeSurface<Error = <<D1 as Device>::Surface as Surface>::Error>,
|
||||
D2: Device + 'static,
|
||||
<D2 as Device>::Surface: NativeSurface<Error = <<D2 as Device>::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<B1, B2, L>(
|
||||
dev: FallbackDevice<D1, D2>,
|
||||
logger: L,
|
||||
) -> Result<FallbackEglDevice<B1, D1, B2, D2>, EglUnderlying<D1, D2>>
|
||||
where
|
||||
B1: Backend<Surface = <D1 as Device>::Surface, Error = <<D1 as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
D1: NativeDisplay<B1, Arguments = EglDeviceArguments>,
|
||||
B2: Backend<Surface = <D2 as Device>::Surface, Error = <<D2 as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
D2: NativeDisplay<B2, Arguments = EglDeviceArguments>,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
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::<D1, D2>::Either(err)),
|
||||
},
|
||||
FallbackDevice::Fallback(eglstream) => match EglDevice::new(eglstream, log) {
|
||||
Ok(dev) => Ok(FallbackDevice::Fallback(dev)),
|
||||
Err(err) => Err(EglUnderlying::<D1, D2>::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<B1, B2, L>(
|
||||
dev: FallbackDevice<D1, D2>,
|
||||
default_attributes: GlAttributes,
|
||||
default_requirements: PixelFormatRequirements,
|
||||
logger: L,
|
||||
) -> Result<FallbackEglDevice<B1, D1, B2, D2>, EglUnderlying<D1, D2>>
|
||||
where
|
||||
B1: Backend<Surface = <D1 as Device>::Surface, Error = <<D1 as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
D1: NativeDisplay<B1, Arguments = EglDeviceArguments>,
|
||||
B2: Backend<Surface = <D2 as Device>::Surface, Error = <<D2 as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
D2: NativeDisplay<B2, Arguments = EglDeviceArguments>,
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
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::<D1, D2>::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::<D1, D2>::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<D1: Device, D2: Device> AsRawFd for FallbackDevice<D1, D2> {
|
||||
fallback_device_impl!(as_raw_fd, &Self, RawFd);
|
||||
|
@ -243,13 +445,14 @@ impl<D1: Device, D2: Device> AsRawFd for FallbackDevice<D1, D2> {
|
|||
impl<D1: Device + BasicDevice, D2: Device + BasicDevice> BasicDevice for FallbackDevice<D1, D2> {}
|
||||
impl<D1: Device + ControlDevice, D2: Device + ControlDevice> ControlDevice for FallbackDevice<D1, D2> {}
|
||||
|
||||
impl<E, C, S1, S2, D1, D2> Device for FallbackDevice<D1, D2>
|
||||
impl<E1, E2, C, S1, S2, D1, D2> Device for FallbackDevice<D1, D2>
|
||||
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<Item = connector::Handle> + 'static,
|
||||
S1: Surface<Error = E, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E, Connectors = C> + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E2, Connectors = C> + 'static,
|
||||
D1: Device<Surface = S1> + 'static,
|
||||
D2: Device<Surface = S2> + 'static,
|
||||
{
|
||||
|
@ -268,18 +471,20 @@ where
|
|||
crtc: crtc::Handle,
|
||||
mode: Mode,
|
||||
connectors: &[connector::Handle],
|
||||
) -> Result<Self::Surface, E> {
|
||||
) -> Result<Self::Surface, EitherError<E1, E2>> {
|
||||
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<ResourceHandles, E>);
|
||||
fallback_device_err_impl!(resource_handles, &Self, Result<ResourceHandles, EitherError<E1, E2>>);
|
||||
fallback_device_impl!(get_connector_info, &Self, Result<connector::Info, DrmError>, conn: connector::Handle);
|
||||
fallback_device_impl!(get_crtc_info, &Self, Result<crtc::Info, DrmError>, crtc: crtc::Handle);
|
||||
fallback_device_impl!(get_encoder_info, &Self, Result<encoder::Info, DrmError>, enc: encoder::Handle);
|
||||
|
@ -288,13 +493,14 @@ where
|
|||
}
|
||||
|
||||
// Impl RawDevice where underlying types implement RawDevice
|
||||
impl<E, C, S1, S2, D1, D2> RawDevice for FallbackDevice<D1, D2>
|
||||
impl<E1, E2, C, S1, S2, D1, D2> RawDevice for FallbackDevice<D1, D2>
|
||||
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<Item = connector::Handle> + 'static,
|
||||
S1: RawSurface + Surface<Error = E, Connectors = C> + 'static,
|
||||
S2: RawSurface + Surface<Error = E, Connectors = C> + 'static,
|
||||
S1: RawSurface + Surface<Error = E1, Connectors = C> + 'static,
|
||||
S2: RawSurface + Surface<Error = E2, Connectors = C> + 'static,
|
||||
D1: RawDevice<Surface = S1> + 'static,
|
||||
D2: RawDevice<Surface = S2> + 'static,
|
||||
{
|
||||
|
@ -308,41 +514,47 @@ impl<D1: Device + EGLGraphicsBackend + 'static, D2: Device + EGLGraphicsBackend
|
|||
fallback_device_impl!(bind_wl_display, &Self, Result<EGLBufferReader, EGLError>, display : &Display);
|
||||
}
|
||||
|
||||
impl<E, C, S1, S2> Surface for FallbackSurface<S1, S2>
|
||||
impl<E1, E2, C, S1, S2> Surface for FallbackSurface<S1, S2>
|
||||
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<Item = connector::Handle> + 'static,
|
||||
S1: Surface<Error = E, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E, Connectors = C> + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + 'static,
|
||||
S2: Surface<Error = E2, Connectors = C> + 'static,
|
||||
{
|
||||
type Error = E;
|
||||
type Error = EitherError<E1, E2>;
|
||||
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<E1, E2>>, conn: connector::Handle);
|
||||
fallback_surface_err_impl!(remove_connector, &Self, Result<(), EitherError<E1, E2>>, conn: connector::Handle);
|
||||
fallback_surface_err_impl!(set_connectors, &Self, Result<(), EitherError<E1, E2>>, 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<E1, E2>>, mode: Mode);
|
||||
}
|
||||
|
||||
impl<E, C, S1, S2> RawSurface for FallbackSurface<S1, S2>
|
||||
impl<E1, E2, C, S1, S2> RawSurface for FallbackSurface<S1, S2>
|
||||
where
|
||||
E: std::error::Error + Send + 'static,
|
||||
E1: std::error::Error + Send + 'static,
|
||||
E2: std::error::Error + Send + 'static,
|
||||
C: IntoIterator<Item = connector::Handle> + 'static,
|
||||
S1: RawSurface + Surface<Error = E, Connectors = C> + 'static,
|
||||
S2: RawSurface + Surface<Error = E, Connectors = C> + 'static,
|
||||
S1: RawSurface + Surface<Error = E1, Connectors = C> + 'static,
|
||||
S2: RawSurface + Surface<Error = E2, Connectors = C> + '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<E1, E2>>, fb: framebuffer::Handle);
|
||||
fn page_flip(&self, framebuffer: framebuffer::Handle) -> Result<(), EitherError<E1, E2>> {
|
||||
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<S1: Surface + AsRawFd, S2: Surface + AsRawFd> AsRawFd for FallbackSurface<S
|
|||
impl<S1: Surface + BasicDevice, S2: Surface + BasicDevice> BasicDevice for FallbackSurface<S1, S2> {}
|
||||
impl<S1: Surface + ControlDevice, S2: Surface + ControlDevice> ControlDevice for FallbackSurface<S1, S2> {}
|
||||
|
||||
impl<E1, E2, C, CF, S1, S2> CursorBackend for FallbackSurface<S1, S2>
|
||||
impl<E1, E2, E3, E4, C, CF, S1, S2> CursorBackend for FallbackSurface<S1, S2>
|
||||
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<Item = connector::Handle> + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + CursorBackend<CursorFormat = CF, Error = E2> + 'static,
|
||||
S2: Surface<Error = E1, Connectors = C> + CursorBackend<CursorFormat = CF, Error = E2> + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + CursorBackend<CursorFormat = CF, Error = E3> + 'static,
|
||||
S2: Surface<Error = E2, Connectors = C> + CursorBackend<CursorFormat = CF, Error = E4> + 'static,
|
||||
{
|
||||
type CursorFormat = CF;
|
||||
type Error = E2;
|
||||
type Error = EitherError<E3, E4>;
|
||||
|
||||
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<E3, E4>>, x: u32, y: u32);
|
||||
fallback_surface_err_impl!(set_cursor_representation, &Self, Result<(), EitherError<E3, E4>>, buffer: &Self::CursorFormat, hotspot: (u32, u32));
|
||||
}
|
||||
|
||||
#[cfg(feature = "renderer_gl")]
|
||||
impl<E, C, S1, S2> GLGraphicsBackend for FallbackSurface<S1, S2>
|
||||
impl<E1, E2, C, S1, S2> GLGraphicsBackend for FallbackSurface<S1, S2>
|
||||
where
|
||||
E: std::error::Error + Send + 'static,
|
||||
E1: std::error::Error + Send + 'static,
|
||||
E2: std::error::Error + Send + 'static,
|
||||
C: IntoIterator<Item = connector::Handle> + 'static,
|
||||
S1: Surface<Error = E, Connectors = C> + GLGraphicsBackend + 'static,
|
||||
S2: Surface<Error = E, Connectors = C> + GLGraphicsBackend + 'static,
|
||||
S1: Surface<Error = E1, Connectors = C> + GLGraphicsBackend + 'static,
|
||||
S2: Surface<Error = E2, Connectors = C> + GLGraphicsBackend + 'static,
|
||||
{
|
||||
fallback_surface_impl!(swap_buffers, &Self, Result<(), SwapBuffersError>);
|
||||
fallback_surface_impl!(get_proc_address, &Self, *const c_void, symbol: &str);
|
||||
|
|
|
@ -46,17 +46,16 @@ pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'sta
|
|||
Underlying(#[source] U),
|
||||
}
|
||||
|
||||
type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
|
||||
pub(crate) type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
|
||||
type BackendRef<D> = Weak<EglSurfaceInternal<<D as Device>::Surface>>;
|
||||
|
||||
/// Representation of an egl device to create egl rendering surfaces
|
||||
pub struct EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = Arguments, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
dev: EGLDisplay<B, D>,
|
||||
logger: ::slog::Logger,
|
||||
|
@ -67,11 +66,10 @@ where
|
|||
|
||||
impl<B, D> AsRawFd for EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = Arguments, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.dev.borrow().as_raw_fd()
|
||||
|
@ -80,11 +78,10 @@ where
|
|||
|
||||
impl<B, D> EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = Arguments, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
/// Try to create a new [`EglDevice`] from an open device.
|
||||
///
|
||||
|
@ -138,22 +135,20 @@ where
|
|||
|
||||
struct InternalDeviceHandler<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = Arguments, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
handler: Box<dyn DeviceHandler<Device = EglDevice<B, D>> + 'static>,
|
||||
}
|
||||
|
||||
impl<B, D> DeviceHandler for InternalDeviceHandler<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = Arguments, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
type Device = D;
|
||||
|
||||
|
@ -167,11 +162,10 @@ where
|
|||
|
||||
impl<B, D> Device for EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = Arguments, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
type Surface = EglSurface<<D as Device>::Surface>;
|
||||
|
||||
|
@ -250,11 +244,10 @@ where
|
|||
#[cfg(feature = "use_system_lib")]
|
||||
impl<B, D> EGLGraphicsBackend for EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = Arguments, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
fn bind_wl_display(&self, display: &Display) -> Result<EGLBufferReader, EGLError> {
|
||||
self.dev.bind_wl_display(display)
|
||||
|
@ -263,11 +256,10 @@ where
|
|||
|
||||
impl<B, D> Drop for EglDevice<B, D>
|
||||
where
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = Arguments, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device + NativeDisplay<B, Arguments = Arguments> + 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
fn drop(&mut self) {
|
||||
self.clear_handler();
|
||||
|
|
|
@ -28,15 +28,13 @@ pub struct EglDeviceObserver<S: SessionObserver + 'static, N: NativeSurface + Su
|
|||
impl<S, B, D> AsSessionObserver<EglDeviceObserver<S, <D as Device>::Surface>> for EglDevice<B, D>
|
||||
where
|
||||
S: SessionObserver + 'static,
|
||||
B: Backend<Surface = <D as Device>::Surface> + 'static,
|
||||
D: Device
|
||||
+ NativeDisplay<
|
||||
B,
|
||||
Arguments = (crtc::Handle, Mode, Vec<connector::Handle>),
|
||||
Error = <<D as Device>::Surface as Surface>::Error,
|
||||
> + AsSessionObserver<S>
|
||||
B: Backend<Surface = <D as Device>::Surface, Error = <<D as Device>::Surface as Surface>::Error>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface,
|
||||
D: Device
|
||||
+ NativeDisplay<B, Arguments = (crtc::Handle, Mode, Vec<connector::Handle>)>
|
||||
+ AsSessionObserver<S>
|
||||
+ 'static,
|
||||
<D as Device>::Surface: NativeSurface<Error = <<D as Device>::Surface as Surface>::Error>,
|
||||
{
|
||||
fn observer(&mut self) -> EglDeviceObserver<S, <D as Device>::Surface> {
|
||||
EglDeviceObserver {
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
//!
|
||||
//! Egl [`NativeDisplay`](::backend::egl::native::NativeDisplay) and
|
||||
//! [`NativeSurface`](::backend::egl::native::NativeSurface) support for
|
||||
//! [`EglStreamDevice`](EglStreamDevice) and [`EglStreamSurface`](EglStreamSurface).
|
||||
//!
|
||||
|
||||
#[cfg(feature = "backend_drm_atomic")]
|
||||
use crate::backend::drm::atomic::AtomicDrmDevice;
|
||||
#[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
|
||||
use crate::backend::drm::common::fallback::{EitherError, FallbackDevice, FallbackSurface};
|
||||
#[cfg(any(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
|
||||
use crate::backend::drm::common::Error as DrmError;
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
use crate::backend::drm::legacy::LegacyDrmDevice;
|
||||
use crate::backend::drm::{Device, RawDevice, RawSurface, Surface};
|
||||
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
|
||||
use crate::backend::egl::{
|
||||
display::EGLDisplayHandle, ffi, wrap_egl_call, EGLError, Error as EglBackendError, SurfaceCreationError,
|
||||
SwapBuffersError,
|
||||
};
|
||||
|
||||
use super::Error;
|
||||
use super::{EglStreamDevice, EglStreamSurface};
|
||||
|
||||
use drm::control::{connector, crtc, Device as ControlDevice, Mode};
|
||||
use nix::libc::{c_int, c_void};
|
||||
use std::marker::PhantomData;
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Egl Device backend type
|
||||
///
|
||||
/// See [`Backend`](::backend::egl::native::Backend).
|
||||
pub struct EglStreamDeviceBackend<D: RawDevice + 'static> {
|
||||
_userdata: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> Backend for EglStreamDeviceBackend<D>
|
||||
where
|
||||
EglStreamSurface<D>: NativeSurface<Error = Error<<<D as Device>::Surface as Surface>::Error>>,
|
||||
{
|
||||
type Surface = EglStreamSurface<D>;
|
||||
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
|
||||
|
||||
unsafe fn get_display<F>(
|
||||
display: ffi::NativeDisplayType,
|
||||
attribs: &[ffi::EGLint],
|
||||
has_dp_extension: F,
|
||||
log: ::slog::Logger,
|
||||
) -> Result<ffi::egl::types::EGLDisplay, EGLError>
|
||||
where
|
||||
F: Fn(&str) -> bool,
|
||||
{
|
||||
if has_dp_extension("EGL_EXT_platform_device") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
|
||||
debug!(
|
||||
log,
|
||||
"EGL Display Initialization via EGL_EXT_platform_device with {:?}", display
|
||||
);
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::GetPlatformDisplayEXT(
|
||||
ffi::egl::PLATFORM_DEVICE_EXT,
|
||||
display as *mut _,
|
||||
attribs.as_ptr() as *const _,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Ok(ffi::egl::NO_DISPLAY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<EglStreamDeviceBackend<D>>
|
||||
for EglStreamDevice<D>
|
||||
where
|
||||
EglStreamSurface<D>: NativeSurface<Error = Error<<<D as Device>::Surface as Surface>::Error>>,
|
||||
{
|
||||
type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
|
||||
|
||||
fn is_backend(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn ptr(&self) -> Result<ffi::NativeDisplayType, EglBackendError> {
|
||||
Ok(self.dev as *const _)
|
||||
}
|
||||
|
||||
fn attributes(&self) -> Vec<ffi::EGLint> {
|
||||
vec![
|
||||
ffi::egl::DRM_MASTER_FD_EXT as ffi::EGLint,
|
||||
self.raw.as_raw_fd(),
|
||||
ffi::egl::NONE as i32,
|
||||
]
|
||||
}
|
||||
|
||||
fn surface_type(&self) -> ffi::EGLint {
|
||||
ffi::egl::STREAM_BIT_KHR as ffi::EGLint
|
||||
}
|
||||
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
args: Self::Arguments,
|
||||
) -> Result<EglStreamSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> {
|
||||
Device::create_surface(self, args.0, args.1, &args.2)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_drm_atomic")]
|
||||
unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<AtomicDrmDevice<A>> {
|
||||
type Error = Error<DrmError>;
|
||||
|
||||
unsafe fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attribs: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Self::Error>> {
|
||||
let output_attributes = {
|
||||
let mut out: Vec<isize> = Vec::with_capacity(3);
|
||||
out.push(ffi::egl::DRM_PLANE_EXT as isize);
|
||||
out.push(Into::<u32>::into(self.0.crtc.0.planes.primary) as isize);
|
||||
out.push(ffi::egl::NONE as isize);
|
||||
out
|
||||
};
|
||||
|
||||
self.create_surface(display, config_id, surface_attribs, &output_attributes)
|
||||
.map_err(SurfaceCreationError::NativeSurfaceCreationFailed)
|
||||
}
|
||||
|
||||
fn needs_recreation(&self) -> bool {
|
||||
self.0.crtc.commit_pending()
|
||||
}
|
||||
|
||||
fn swap_buffers(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
) -> Result<(), SwapBuffersError<Error<DrmError>>> {
|
||||
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
|
||||
let _ = self.0.crtc.destroy_framebuffer(fb);
|
||||
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
|
||||
}
|
||||
|
||||
self.flip(self.0.crtc.0.crtc, display, surface)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
unsafe impl<A: AsRawFd + 'static> NativeSurface for EglStreamSurface<LegacyDrmDevice<A>> {
|
||||
type Error = Error<DrmError>;
|
||||
|
||||
unsafe fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attribs: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Self::Error>> {
|
||||
let output_attributes = {
|
||||
let mut out: Vec<isize> = Vec::with_capacity(3);
|
||||
out.push(ffi::egl::DRM_CRTC_EXT as isize);
|
||||
out.push(Into::<u32>::into(self.0.crtc.0.crtc) as isize);
|
||||
out.push(ffi::egl::NONE as isize);
|
||||
out
|
||||
};
|
||||
|
||||
self.create_surface(display, config_id, surface_attribs, &output_attributes)
|
||||
.map_err(SurfaceCreationError::NativeSurfaceCreationFailed)
|
||||
}
|
||||
|
||||
fn needs_recreation(&self) -> bool {
|
||||
self.0.crtc.commit_pending()
|
||||
}
|
||||
|
||||
fn swap_buffers(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
) -> Result<(), SwapBuffersError<Error<DrmError>>> {
|
||||
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
|
||||
let _ = self.0.crtc.destroy_framebuffer(fb);
|
||||
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
|
||||
}
|
||||
self.flip(self.0.crtc.0.crtc, display, surface)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "backend_drm_atomic", feature = "backend_drm_legacy"))]
|
||||
unsafe impl<A: AsRawFd + 'static> NativeSurface
|
||||
for EglStreamSurface<FallbackDevice<AtomicDrmDevice<A>, LegacyDrmDevice<A>>>
|
||||
{
|
||||
type Error = Error<EitherError<DrmError, DrmError>>;
|
||||
|
||||
unsafe fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attribs: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Self::Error>> {
|
||||
let output_attributes = {
|
||||
let mut out: Vec<isize> = Vec::with_capacity(3);
|
||||
match &self.0.crtc {
|
||||
FallbackSurface::Preference(dev) => {
|
||||
out.push(ffi::egl::DRM_PLANE_EXT as isize);
|
||||
out.push(Into::<u32>::into(dev.0.planes.primary) as isize);
|
||||
} //AtomicDrmSurface
|
||||
FallbackSurface::Fallback(dev) => {
|
||||
out.push(ffi::egl::DRM_CRTC_EXT as isize);
|
||||
out.push(Into::<u32>::into(dev.0.crtc) as isize);
|
||||
} // LegacyDrmSurface
|
||||
}
|
||||
out.push(ffi::egl::NONE as isize);
|
||||
out
|
||||
};
|
||||
|
||||
self.create_surface(display, config_id, surface_attribs, &output_attributes)
|
||||
.map_err(SurfaceCreationError::NativeSurfaceCreationFailed)
|
||||
}
|
||||
|
||||
fn needs_recreation(&self) -> bool {
|
||||
self.0.crtc.commit_pending()
|
||||
}
|
||||
|
||||
fn swap_buffers(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
) -> Result<(), SwapBuffersError<Self::Error>> {
|
||||
if let Some((buffer, fb)) = self.0.commit_buffer.take() {
|
||||
let _ = self.0.crtc.destroy_framebuffer(fb);
|
||||
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
|
||||
}
|
||||
let crtc = match &self.0.crtc {
|
||||
FallbackSurface::Preference(dev) => dev.0.crtc,
|
||||
FallbackSurface::Fallback(dev) => dev.0.crtc,
|
||||
};
|
||||
|
||||
self.flip(crtc, display, surface)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,364 @@
|
|||
//!
|
||||
//! [`Device`](Device) and [`Surface`](Surface) implementations using
|
||||
//! the proposed EGLStream api for efficient rendering.
|
||||
//! Currently this api is only implemented by the proprietary `nvidia` driver.
|
||||
//!
|
||||
//! Usually this implementation will be wrapped into a [`EglDevice`](::backend::drm::egl::EglDevice).
|
||||
//!
|
||||
//! To use these types standalone, you will need to render via egl yourself as page flips
|
||||
//! are driven via `eglSwapBuffers`.
|
||||
//!
|
||||
//! To use this api in place of GBM for nvidia cards take a look at
|
||||
//! [`FallbackDevice`](::backend::drm::common::fallback::FallbackDevice).
|
||||
//! Take a look at `anvil`s source code for an example of this.
|
||||
//!
|
||||
|
||||
use super::{Device, DeviceHandler, RawDevice, Surface};
|
||||
|
||||
use drm::buffer::format::PixelFormat;
|
||||
use drm::control::{
|
||||
connector, crtc, encoder, framebuffer, plane, Device as ControlDevice, Mode, ResourceHandles,
|
||||
};
|
||||
use drm::SystemError as DrmError;
|
||||
use failure::ResultExt;
|
||||
use nix::libc::dev_t;
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::{fs, ptr};
|
||||
|
||||
mod surface;
|
||||
pub use self::surface::EglStreamSurface;
|
||||
use self::surface::EglStreamSurfaceInternal;
|
||||
|
||||
pub mod egl;
|
||||
#[cfg(feature = "backend_session")]
|
||||
pub mod session;
|
||||
|
||||
use crate::backend::egl::ffi::{self, egl::types::EGLDeviceEXT};
|
||||
use crate::backend::egl::{wrap_egl_call, EGLError as RawEGLError, Error as EglError};
|
||||
use crate::backend::graphics::SwapBuffersError;
|
||||
|
||||
/// Errors thrown by the [`EglStreamDevice`](::backend::drm::eglstream::EglStreamDevice)
|
||||
/// and [`EglStreamSurface`](::backend::drm::eglstream::EglStreamSurface).
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error<U: std::error::Error + std::fmt::Debug + std::fmt::Display + 'static> {
|
||||
/// `eglInitialize` returned an error
|
||||
#[error("Failed to initialize EGL: {0:}")]
|
||||
InitFailed(#[source] RawEGLError),
|
||||
/// Failed to enumerate EGL_EXT_drm_device
|
||||
#[error("Failed to enumerate EGL_EXT_drm_device: {0:}")]
|
||||
FailedToEnumerateDevices(#[source] RawEGLError),
|
||||
/// Device is not compatible with EGL_EXT_drm_device extension
|
||||
#[error("Device is not compatible with EGL_EXT_drm_device extension")]
|
||||
DeviceIsNoEGLStreamDevice,
|
||||
/// Device has not suitable output layer
|
||||
#[error("Device has no suitable output layer")]
|
||||
DeviceNoOutputLayer,
|
||||
/// Device was unable to create an EGLStream
|
||||
#[error("EGLStream creation failed")]
|
||||
DeviceStreamCreationFailed,
|
||||
/// Underlying backend error
|
||||
#[error("Underlying error: {0}")]
|
||||
Underlying(#[source] U),
|
||||
/// Buffer creation failed
|
||||
#[error("Buffer creation failed")]
|
||||
BufferCreationFailed(#[source] failure::Compat<DrmError>),
|
||||
/// Buffer write failed
|
||||
#[error("Buffer write failed")]
|
||||
BufferWriteFailed(#[source] failure::Compat<DrmError>),
|
||||
/// Stream flip failed
|
||||
#[error("Stream flip failed ({0})")]
|
||||
StreamFlipFailed(#[source] RawEGLError),
|
||||
}
|
||||
|
||||
/// Representation of an open egl stream device to create egl rendering surfaces
|
||||
pub struct EglStreamDevice<D: RawDevice + ControlDevice + 'static> {
|
||||
pub(self) dev: EGLDeviceEXT,
|
||||
raw: D,
|
||||
backends: Rc<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> EglStreamDevice<D> {
|
||||
/// Try to create a new [`EglStreamDevice`] from an open device.
|
||||
///
|
||||
/// Returns an error if the underlying device would not support the required EGL extensions.
|
||||
pub fn new<L>(mut raw: D, logger: L) -> Result<Self, Error<<<D as Device>::Surface as Surface>::Error>>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
let log = crate::slog_or_stdlog(logger).new(o!("smithay_module" => "backend_eglstream"));
|
||||
|
||||
raw.clear_handler();
|
||||
|
||||
debug!(log, "Creating egl stream device");
|
||||
|
||||
ffi::make_sure_egl_is_loaded();
|
||||
|
||||
fn has_extensions(exts: &[String], check: &'static [&'static str]) -> Result<(), EglError> {
|
||||
check
|
||||
.iter()
|
||||
.map(|ext| {
|
||||
if exts.iter().any(|s| *s == *ext) {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EglError::EglExtensionNotSupported(check))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
let device = unsafe {
|
||||
// the first step is to query the list of extensions without any display, if supported
|
||||
let dp_extensions = {
|
||||
let p = wrap_egl_call(|| {
|
||||
ffi::egl::QueryString(ffi::egl::NO_DISPLAY, ffi::egl::EXTENSIONS as i32)
|
||||
})
|
||||
.map_err(Error::InitFailed)?;
|
||||
|
||||
// this possibility is available only with EGL 1.5 or EGL_EXT_platform_base, otherwise
|
||||
// `eglQueryString` returns an error
|
||||
if p.is_null() {
|
||||
vec![]
|
||||
} else {
|
||||
let p = CStr::from_ptr(p);
|
||||
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
|
||||
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||
}
|
||||
};
|
||||
debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions);
|
||||
|
||||
// we need either EGL_EXT_device_base or EGL_EXT_device_enumeration &_query
|
||||
if let Err(_err) = has_extensions(&dp_extensions, &["EGL_EXT_device_base"]) {
|
||||
has_extensions(
|
||||
&dp_extensions,
|
||||
&["EGL_EXT_device_enumeration", "EGL_EXT_device_query"],
|
||||
)
|
||||
.map_err(|_| Error::DeviceIsNoEGLStreamDevice)?;
|
||||
}
|
||||
|
||||
let mut num_devices = 0;
|
||||
wrap_egl_call(|| ffi::egl::QueryDevicesEXT(0, ptr::null_mut(), &mut num_devices))
|
||||
.map_err(Error::FailedToEnumerateDevices)?;
|
||||
if num_devices == 0 {
|
||||
return Err(Error::DeviceIsNoEGLStreamDevice);
|
||||
}
|
||||
|
||||
let mut devices = Vec::with_capacity(num_devices as usize);
|
||||
wrap_egl_call(|| ffi::egl::QueryDevicesEXT(num_devices, devices.as_mut_ptr(), &mut num_devices))
|
||||
.map_err(Error::FailedToEnumerateDevices)?;
|
||||
devices.set_len(num_devices as usize);
|
||||
debug!(log, "Devices: {:#?}, Count: {}", devices, num_devices);
|
||||
|
||||
devices
|
||||
.into_iter()
|
||||
.find(|device| {
|
||||
*device != ffi::egl::NO_DEVICE_EXT
|
||||
&& {
|
||||
let device_extensions = {
|
||||
let p = ffi::egl::QueryDeviceStringEXT(*device, ffi::egl::EXTENSIONS as i32);
|
||||
if p.is_null() {
|
||||
vec![]
|
||||
} else {
|
||||
let p = CStr::from_ptr(p);
|
||||
let list = String::from_utf8(p.to_bytes().to_vec())
|
||||
.unwrap_or_else(|_| String::new());
|
||||
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||
}
|
||||
};
|
||||
|
||||
device_extensions.iter().any(|s| *s == "EGL_EXT_device_drm")
|
||||
}
|
||||
&& {
|
||||
let path = {
|
||||
let p = ffi::egl::QueryDeviceStringEXT(
|
||||
*device,
|
||||
ffi::egl::DRM_DEVICE_FILE_EXT as i32,
|
||||
);
|
||||
if p.is_null() {
|
||||
String::new()
|
||||
} else {
|
||||
let p = CStr::from_ptr(p);
|
||||
String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new())
|
||||
}
|
||||
};
|
||||
|
||||
match fs::metadata(&path) {
|
||||
Ok(metadata) => metadata.rdev() == raw.device_id(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok_or(Error::DeviceIsNoEGLStreamDevice)?
|
||||
};
|
||||
|
||||
Ok(EglStreamDevice {
|
||||
dev: device,
|
||||
raw,
|
||||
backends: Rc::new(RefCell::new(HashMap::new())),
|
||||
logger: log,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct InternalDeviceHandler<D: RawDevice + ControlDevice + 'static> {
|
||||
handler: Box<dyn DeviceHandler<Device = EglStreamDevice<D>> + 'static>,
|
||||
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> DeviceHandler for InternalDeviceHandler<D> {
|
||||
type Device = D;
|
||||
|
||||
fn vblank(&mut self, crtc: crtc::Handle) {
|
||||
if let Some(backends) = self.backends.upgrade() {
|
||||
if let Some(surface) = backends.borrow().get(&crtc) {
|
||||
if surface.upgrade().is_some() {
|
||||
self.handler.vblank(crtc);
|
||||
}
|
||||
} else {
|
||||
warn!(
|
||||
self.logger,
|
||||
"Surface ({:?}) not managed by egl stream, event not handled.", crtc
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn error(&mut self, error: <<D as Device>::Surface as Surface>::Error) {
|
||||
self.handler.error(Error::Underlying(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> Device for EglStreamDevice<D> {
|
||||
type Surface = EglStreamSurface<D>;
|
||||
|
||||
fn device_id(&self) -> dev_t {
|
||||
self.raw.device_id()
|
||||
}
|
||||
|
||||
fn set_handler(&mut self, handler: impl DeviceHandler<Device = Self> + 'static) {
|
||||
self.raw.set_handler(InternalDeviceHandler {
|
||||
handler: Box::new(handler),
|
||||
backends: Rc::downgrade(&self.backends),
|
||||
logger: self.logger.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
fn clear_handler(&mut self) {
|
||||
self.raw.clear_handler();
|
||||
}
|
||||
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
crtc: crtc::Handle,
|
||||
mode: Mode,
|
||||
connectors: &[connector::Handle],
|
||||
) -> Result<EglStreamSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> {
|
||||
info!(self.logger, "Initializing EglStreamSurface");
|
||||
|
||||
let drm_surface =
|
||||
Device::create_surface(&mut self.raw, crtc, mode, connectors).map_err(Error::Underlying)?;
|
||||
|
||||
// initialize a buffer for the cursor image
|
||||
let cursor = Cell::new(Some((
|
||||
self.raw
|
||||
.create_dumb_buffer((1, 1), PixelFormat::ARGB8888)
|
||||
.compat()
|
||||
.map_err(Error::BufferCreationFailed)?,
|
||||
(0, 0),
|
||||
)));
|
||||
|
||||
let backend = Rc::new(EglStreamSurfaceInternal {
|
||||
crtc: drm_surface,
|
||||
cursor,
|
||||
stream: RefCell::new(None),
|
||||
commit_buffer: Cell::new(None),
|
||||
logger: self.logger.new(o!("crtc" => format!("{:?}", crtc))),
|
||||
locked: std::sync::atomic::AtomicBool::new(false),
|
||||
});
|
||||
self.backends.borrow_mut().insert(crtc, Rc::downgrade(&backend));
|
||||
Ok(EglStreamSurface(backend))
|
||||
}
|
||||
|
||||
fn process_events(&mut self) {
|
||||
self.raw.process_events()
|
||||
}
|
||||
|
||||
fn resource_handles(&self) -> Result<ResourceHandles, Error<<<D as Device>::Surface as Surface>::Error>> {
|
||||
Device::resource_handles(&self.raw).map_err(Error::Underlying)
|
||||
}
|
||||
|
||||
fn get_connector_info(&self, conn: connector::Handle) -> Result<connector::Info, DrmError> {
|
||||
self.raw.get_connector_info(conn)
|
||||
}
|
||||
fn get_crtc_info(&self, crtc: crtc::Handle) -> Result<crtc::Info, DrmError> {
|
||||
self.raw.get_crtc_info(crtc)
|
||||
}
|
||||
fn get_encoder_info(&self, enc: encoder::Handle) -> Result<encoder::Info, DrmError> {
|
||||
self.raw.get_encoder_info(enc)
|
||||
}
|
||||
fn get_framebuffer_info(&self, fb: framebuffer::Handle) -> Result<framebuffer::Info, DrmError> {
|
||||
self.raw.get_framebuffer_info(fb)
|
||||
}
|
||||
fn get_plane_info(&self, plane: plane::Handle) -> Result<plane::Info, DrmError> {
|
||||
self.raw.get_plane_info(plane)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> AsRawFd for EglStreamDevice<D> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.raw.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + ControlDevice + 'static> Drop for EglStreamDevice<D> {
|
||||
fn drop(&mut self) {
|
||||
self.clear_handler();
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Into<SwapBuffersError> for Error<E>
|
||||
where
|
||||
E: std::error::Error + Into<SwapBuffersError> + 'static,
|
||||
{
|
||||
fn into(self) -> SwapBuffersError {
|
||||
match self {
|
||||
Error::BufferCreationFailed(x)
|
||||
if match x.get_ref() {
|
||||
drm::SystemError::Unknown {
|
||||
errno: nix::errno::Errno::EBUSY,
|
||||
} => true,
|
||||
drm::SystemError::Unknown {
|
||||
errno: nix::errno::Errno::EINTR,
|
||||
} => true,
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
SwapBuffersError::TemporaryFailure(Box::new(Error::<E>::BufferCreationFailed(x)))
|
||||
}
|
||||
Error::BufferWriteFailed(x)
|
||||
if match x.get_ref() {
|
||||
drm::SystemError::Unknown {
|
||||
errno: nix::errno::Errno::EBUSY,
|
||||
} => true,
|
||||
drm::SystemError::Unknown {
|
||||
errno: nix::errno::Errno::EINTR,
|
||||
} => true,
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
SwapBuffersError::TemporaryFailure(Box::new(Error::<E>::BufferCreationFailed(x)))
|
||||
}
|
||||
Error::StreamFlipFailed(x @ RawEGLError::ResourceBusy) => {
|
||||
SwapBuffersError::TemporaryFailure(Box::new(Error::<E>::StreamFlipFailed(x)))
|
||||
}
|
||||
Error::Underlying(x) => x.into(),
|
||||
x => SwapBuffersError::ContextLost(Box::new(x)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//!
|
||||
//! Support to register a [`EglStreamDevice`](EglStreamDevice)
|
||||
//! to an open [`Session`](::backend::session::Session).
|
||||
//!
|
||||
|
||||
use super::{EglStreamDevice, EglStreamSurfaceInternal};
|
||||
use crate::backend::drm::{RawDevice, Surface};
|
||||
use crate::backend::egl::ffi;
|
||||
use crate::backend::session::{AsSessionObserver, SessionObserver};
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use drm::control::{crtc, Device as ControlDevice};
|
||||
|
||||
/// [`SessionObserver`](SessionObserver)
|
||||
/// linked to the [`EglStreamDevice`](EglStreamDevice) it was
|
||||
/// created from.
|
||||
pub struct EglStreamDeviceObserver<
|
||||
O: SessionObserver + 'static,
|
||||
D: RawDevice + AsSessionObserver<O> + 'static,
|
||||
> {
|
||||
observer: O,
|
||||
backends: Weak<RefCell<HashMap<crtc::Handle, Weak<EglStreamSurfaceInternal<D>>>>>,
|
||||
logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<O: SessionObserver + 'static, D: RawDevice + ControlDevice + AsSessionObserver<O> + 'static>
|
||||
AsSessionObserver<EglStreamDeviceObserver<O, D>> for EglStreamDevice<D>
|
||||
{
|
||||
fn observer(&mut self) -> EglStreamDeviceObserver<O, D> {
|
||||
EglStreamDeviceObserver {
|
||||
observer: self.raw.observer(),
|
||||
backends: Rc::downgrade(&self.backends),
|
||||
logger: self.logger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: SessionObserver + 'static, D: RawDevice + AsSessionObserver<O> + 'static> SessionObserver
|
||||
for EglStreamDeviceObserver<O, D>
|
||||
{
|
||||
fn pause(&mut self, devnum: Option<(u32, u32)>) {
|
||||
if let Some(backends) = self.backends.upgrade() {
|
||||
for (_, backend) in backends.borrow().iter() {
|
||||
if let Some(backend) = backend.upgrade() {
|
||||
// destroy/disable the streams so it will not submit any pending frames
|
||||
if let Some((display, stream)) = backend.stream.replace(None) {
|
||||
unsafe {
|
||||
ffi::egl::DestroyStreamKHR(display.handle, stream);
|
||||
}
|
||||
}
|
||||
// framebuffers will be likely not valid anymore, lets just recreate those after activation.
|
||||
if let Some((buffer, fb)) = backend.commit_buffer.take() {
|
||||
let _ = backend.crtc.destroy_framebuffer(fb);
|
||||
let _ = backend.crtc.destroy_dumb_buffer(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.observer.pause(devnum);
|
||||
}
|
||||
|
||||
fn activate(&mut self, devnum: Option<(u32, u32, Option<RawFd>)>) {
|
||||
self.observer.activate(devnum);
|
||||
if let Some(backends) = self.backends.upgrade() {
|
||||
for (_, backend) in backends.borrow().iter() {
|
||||
if let Some(backend) = backend.upgrade() {
|
||||
if let Some((cursor, hotspot)) = backend.cursor.get() {
|
||||
if backend
|
||||
.crtc
|
||||
.set_cursor2(
|
||||
backend.crtc.crtc(),
|
||||
Some(&cursor),
|
||||
(hotspot.0 as i32, hotspot.1 as i32),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
if let Err(err) = backend.crtc.set_cursor(backend.crtc.crtc(), Some(&cursor)) {
|
||||
warn!(self.logger, "Failed to reset cursor: {}", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,528 @@
|
|||
use super::super::{Device, RawDevice, RawSurface, Surface};
|
||||
use super::Error;
|
||||
|
||||
use drm::buffer::format::PixelFormat;
|
||||
use drm::control::{connector, crtc, dumbbuffer::DumbBuffer, framebuffer, Device as ControlDevice, Mode};
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use failure::ResultExt;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use image::{ImageBuffer, Rgba};
|
||||
use nix::libc::{c_int, c_void};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::ffi::CStr;
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use crate::backend::drm::common::Error as DrmError;
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use crate::backend::drm::DevPath;
|
||||
use crate::backend::egl::ffi::{
|
||||
self,
|
||||
egl::{self, types::EGLStreamKHR},
|
||||
};
|
||||
use crate::backend::egl::{display::EGLDisplayHandle, wrap_egl_call, EGLError, SwapBuffersError};
|
||||
#[cfg(feature = "backend_drm")]
|
||||
use crate::backend::graphics::CursorBackend;
|
||||
|
||||
pub(in crate::backend::drm) struct EglStreamSurfaceInternal<D: RawDevice + 'static> {
|
||||
pub(in crate::backend::drm) crtc: <D as Device>::Surface,
|
||||
pub(in crate::backend::drm) cursor: Cell<Option<(DumbBuffer, (u32, u32))>>,
|
||||
pub(in crate::backend::drm) stream: RefCell<Option<(Arc<EGLDisplayHandle>, EGLStreamKHR)>>,
|
||||
pub(in crate::backend::drm) commit_buffer: Cell<Option<(DumbBuffer, framebuffer::Handle)>>,
|
||||
pub(in crate::backend::drm) locked: AtomicBool,
|
||||
pub(in crate::backend::drm) logger: ::slog::Logger,
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> Surface for EglStreamSurfaceInternal<D> {
|
||||
type Connectors = <<D as Device>::Surface as Surface>::Connectors;
|
||||
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
|
||||
|
||||
fn crtc(&self) -> crtc::Handle {
|
||||
self.crtc.crtc()
|
||||
}
|
||||
|
||||
fn current_connectors(&self) -> Self::Connectors {
|
||||
self.crtc.current_connectors()
|
||||
}
|
||||
|
||||
fn pending_connectors(&self) -> Self::Connectors {
|
||||
self.crtc.pending_connectors()
|
||||
}
|
||||
|
||||
fn add_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
|
||||
self.crtc.add_connector(connector).map_err(Error::Underlying)
|
||||
}
|
||||
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
|
||||
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) -> Mode {
|
||||
self.crtc.current_mode()
|
||||
}
|
||||
|
||||
fn pending_mode(&self) -> Mode {
|
||||
self.crtc.pending_mode()
|
||||
}
|
||||
|
||||
fn use_mode(&self, mode: Mode) -> Result<(), Error<<<D as Device>::Surface as Surface>::Error>> {
|
||||
self.crtc.use_mode(mode).map_err(Error::Underlying)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> Drop for EglStreamSurfaceInternal<D> {
|
||||
fn drop(&mut self) {
|
||||
if let Some((buffer, _)) = self.cursor.get() {
|
||||
let _ = self.crtc.destroy_dumb_buffer(buffer);
|
||||
}
|
||||
if let Some((buffer, fb)) = self.commit_buffer.take() {
|
||||
let _ = self.crtc.destroy_framebuffer(fb);
|
||||
let _ = self.crtc.destroy_dumb_buffer(buffer);
|
||||
}
|
||||
if let Some((display, stream)) = self.stream.replace(None) {
|
||||
unsafe {
|
||||
egl::DestroyStreamKHR(display.handle, stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Conceptionally EglStream is a weird api.
|
||||
// It does modesetting on its own, bypassing our `RawSurface::commit` function.
|
||||
// As a result, we cannot easily sync any other planes to the commit without more
|
||||
// experimental egl extensions to do this via the EglStream-API.
|
||||
//
|
||||
// So instead we leverage the fact, that all drm-drivers still support the legacy
|
||||
// `drmModeSetCursor` and `drmModeMoveCursor` functions, that (mostly) implicitly sync to the primary plane.
|
||||
// That way we can use hardware cursors at least on all drm-backends (including atomic), although
|
||||
// this is a little hacky. Overlay planes however are completely out of question for now.
|
||||
//
|
||||
// Note that this might still appear a little chuppy, we should just use software cursors
|
||||
// on eglstream devices by default and only use this, if the user really wants it.
|
||||
#[cfg(feature = "backend_drm")]
|
||||
impl<D: RawDevice + 'static> CursorBackend for EglStreamSurfaceInternal<D> {
|
||||
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
|
||||
type Error = Error<DrmError>;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> {
|
||||
trace!(self.logger, "Move the cursor to {},{}", x, y);
|
||||
self.crtc
|
||||
.move_cursor(self.crtc.crtc(), (x as i32, y as i32))
|
||||
.compat()
|
||||
.map_err(|source| DrmError::Access {
|
||||
errmsg: "Error moving cursor",
|
||||
dev: self.crtc.dev_path(),
|
||||
source,
|
||||
})
|
||||
.map_err(Error::Underlying)
|
||||
}
|
||||
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Self::Error> {
|
||||
let (w, h) = buffer.dimensions();
|
||||
debug!(self.logger, "Importing cursor");
|
||||
|
||||
// import the cursor into a buffer we can render
|
||||
let mut cursor = self
|
||||
.crtc
|
||||
.create_dumb_buffer((w, h), PixelFormat::ARGB8888)
|
||||
.compat()
|
||||
.map_err(Error::BufferCreationFailed)?;
|
||||
|
||||
{
|
||||
let mut mapping = self
|
||||
.crtc
|
||||
.map_dumb_buffer(&mut cursor)
|
||||
.compat()
|
||||
.map_err(Error::BufferWriteFailed)?;
|
||||
mapping.as_mut().copy_from_slice(buffer);
|
||||
}
|
||||
|
||||
trace!(self.logger, "Setting the new imported cursor");
|
||||
|
||||
trace!(self.logger, "Setting the new imported cursor");
|
||||
|
||||
if self
|
||||
.crtc
|
||||
.set_cursor2(
|
||||
self.crtc.crtc(),
|
||||
Some(&cursor),
|
||||
(hotspot.0 as i32, hotspot.1 as i32),
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
self.crtc
|
||||
.set_cursor(self.crtc.crtc(), Some(&cursor))
|
||||
.compat()
|
||||
.map_err(|source| DrmError::Access {
|
||||
errmsg: "Failed to set cursor",
|
||||
dev: self.crtc.dev_path(),
|
||||
source,
|
||||
})
|
||||
.map_err(Error::Underlying)?;
|
||||
}
|
||||
|
||||
// and store it
|
||||
if let Some((old, _)) = self.cursor.replace(Some((cursor, hotspot))) {
|
||||
if self.crtc.destroy_dumb_buffer(old).is_err() {
|
||||
warn!(self.logger, "Failed to free old cursor");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// egl stream surface for rendering
|
||||
pub struct EglStreamSurface<D: RawDevice + 'static>(
|
||||
pub(in crate::backend::drm) Rc<EglStreamSurfaceInternal<D>>,
|
||||
);
|
||||
|
||||
impl<D: RawDevice + 'static> EglStreamSurface<D> {
|
||||
/// Check if underlying gbm resources need to be recreated.
|
||||
pub fn needs_recreation(&self) -> bool {
|
||||
self.0.crtc.commit_pending() || self.0.stream.borrow().is_none()
|
||||
}
|
||||
|
||||
pub(super) fn create_stream(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
output_attribs: &[isize],
|
||||
) -> Result<EGLStreamKHR, Error<<<D as Device>::Surface as Surface>::Error>> {
|
||||
// drop old steam, if it already exists
|
||||
if let Some((display, stream)) = self.0.stream.replace(None) {
|
||||
// ignore result
|
||||
unsafe {
|
||||
ffi::egl::DestroyStreamKHR(display.handle, stream);
|
||||
}
|
||||
}
|
||||
|
||||
// because we are re-creating there might be a new mode. if there is? -> commit it
|
||||
if self.0.crtc.commit_pending() {
|
||||
let (w, h) = self.pending_mode().size();
|
||||
// but we need a buffer to commit...
|
||||
// well lets create one and clean it up once the stream is running
|
||||
if let Ok(buffer) = self
|
||||
.0
|
||||
.crtc
|
||||
.create_dumb_buffer((w as u32, h as u32), PixelFormat::ARGB8888)
|
||||
{
|
||||
if let Ok(fb) = self.0.crtc.add_framebuffer(&buffer) {
|
||||
if let Some((buffer, fb)) = self.0.commit_buffer.replace(Some((buffer, fb))) {
|
||||
let _ = self.0.crtc.destroy_framebuffer(fb);
|
||||
let _ = self.0.crtc.destroy_dumb_buffer(buffer);
|
||||
}
|
||||
self.0.crtc.commit(fb).map_err(Error::Underlying)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let extensions = {
|
||||
let p =
|
||||
unsafe { CStr::from_ptr(ffi::egl::QueryString(display.handle, ffi::egl::EXTENSIONS as i32)) };
|
||||
let list = String::from_utf8(p.to_bytes().to_vec()).unwrap_or_else(|_| String::new());
|
||||
list.split(' ').map(|e| e.to_string()).collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
if !extensions.iter().any(|s| *s == "EGL_EXT_output_base")
|
||||
|| !extensions.iter().any(|s| *s == "EGL_EXT_output_drm")
|
||||
|| !extensions.iter().any(|s| *s == "EGL_KHR_stream")
|
||||
|| !extensions
|
||||
.iter()
|
||||
.any(|s| *s == "EGL_EXT_stream_consumer_egloutput")
|
||||
|| !extensions
|
||||
.iter()
|
||||
.any(|s| *s == "EGL_KHR_stream_producer_eglsurface")
|
||||
{
|
||||
error!(self.0.logger, "Extension for EGLStream surface creation missing");
|
||||
return Err(Error::DeviceIsNoEGLStreamDevice);
|
||||
}
|
||||
|
||||
if cfg!(debug_assertions) {
|
||||
// TEST START
|
||||
let mut num_layers = 0;
|
||||
if unsafe {
|
||||
ffi::egl::GetOutputLayersEXT(
|
||||
display.handle,
|
||||
ptr::null(),
|
||||
ptr::null_mut(),
|
||||
10,
|
||||
&mut num_layers,
|
||||
)
|
||||
} == 0
|
||||
{
|
||||
error!(self.0.logger, "Failed to get any! output layer");
|
||||
}
|
||||
if num_layers == 0 {
|
||||
error!(self.0.logger, "Failed to find any! output layer");
|
||||
}
|
||||
let mut layers = Vec::with_capacity(num_layers as usize);
|
||||
if unsafe {
|
||||
ffi::egl::GetOutputLayersEXT(
|
||||
display.handle,
|
||||
ptr::null(),
|
||||
layers.as_mut_ptr(),
|
||||
num_layers,
|
||||
&mut num_layers,
|
||||
)
|
||||
} == 0
|
||||
{
|
||||
error!(self.0.logger, "Failed to receive Output Layers");
|
||||
}
|
||||
unsafe {
|
||||
layers.set_len(num_layers as usize);
|
||||
}
|
||||
for layer in layers {
|
||||
debug!(self.0.logger, "Found layer: {:?}", layer);
|
||||
let mut val = 0;
|
||||
if unsafe {
|
||||
ffi::egl::QueryOutputLayerAttribEXT(
|
||||
display.handle,
|
||||
layer,
|
||||
ffi::egl::DRM_CRTC_EXT as i32,
|
||||
&mut val,
|
||||
)
|
||||
} != 0
|
||||
{
|
||||
info!(self.0.logger, "Possible crtc output layer: {}", val);
|
||||
}
|
||||
val = 0;
|
||||
if unsafe {
|
||||
ffi::egl::QueryOutputLayerAttribEXT(
|
||||
display.handle,
|
||||
layer,
|
||||
ffi::egl::DRM_PLANE_EXT as i32,
|
||||
&mut val,
|
||||
)
|
||||
} != 0
|
||||
{
|
||||
info!(self.0.logger, "Possible plane output layer: {}", val);
|
||||
}
|
||||
}
|
||||
// TEST END
|
||||
}
|
||||
|
||||
let mut num_layers = 0;
|
||||
if unsafe {
|
||||
ffi::egl::GetOutputLayersEXT(
|
||||
display.handle,
|
||||
output_attribs.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
1,
|
||||
&mut num_layers,
|
||||
)
|
||||
} == 0
|
||||
{
|
||||
error!(
|
||||
self.0.logger,
|
||||
"Failed to acquire Output Layer. Attributes {:?}", output_attribs
|
||||
);
|
||||
return Err(Error::DeviceNoOutputLayer);
|
||||
}
|
||||
if num_layers == 0 {
|
||||
error!(self.0.logger, "Failed to find Output Layer");
|
||||
return Err(Error::DeviceNoOutputLayer);
|
||||
}
|
||||
let mut layers = Vec::with_capacity(num_layers as usize);
|
||||
if unsafe {
|
||||
ffi::egl::GetOutputLayersEXT(
|
||||
display.handle,
|
||||
output_attribs.as_ptr(),
|
||||
layers.as_mut_ptr(),
|
||||
num_layers,
|
||||
&mut num_layers,
|
||||
)
|
||||
} == 0
|
||||
{
|
||||
error!(self.0.logger, "Failed to get Output Layer");
|
||||
return Err(Error::DeviceNoOutputLayer);
|
||||
}
|
||||
unsafe {
|
||||
layers.set_len(num_layers as usize);
|
||||
}
|
||||
|
||||
let layer = layers[0];
|
||||
unsafe {
|
||||
ffi::egl::OutputLayerAttribEXT(display.handle, layer, ffi::egl::SWAP_INTERVAL_EXT as i32, 0);
|
||||
}
|
||||
|
||||
let stream_attributes = {
|
||||
let mut out: Vec<c_int> = Vec::with_capacity(7);
|
||||
out.push(ffi::egl::STREAM_FIFO_LENGTH_KHR as i32);
|
||||
out.push(0);
|
||||
out.push(ffi::egl::CONSUMER_AUTO_ACQUIRE_EXT as i32);
|
||||
out.push(ffi::egl::FALSE as i32);
|
||||
out.push(ffi::egl::CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR as i32);
|
||||
out.push(0);
|
||||
out.push(ffi::egl::NONE as i32);
|
||||
out
|
||||
};
|
||||
|
||||
let stream = unsafe { ffi::egl::CreateStreamKHR(display.handle, stream_attributes.as_ptr()) };
|
||||
if stream == ffi::egl::NO_STREAM_KHR {
|
||||
error!(self.0.logger, "Failed to create egl stream");
|
||||
return Err(Error::DeviceStreamCreationFailed);
|
||||
}
|
||||
|
||||
if unsafe { ffi::egl::StreamConsumerOutputEXT(display.handle, stream, layer) } == 0 {
|
||||
error!(self.0.logger, "Failed to link Output Layer as Stream Consumer");
|
||||
return Err(Error::DeviceStreamCreationFailed);
|
||||
}
|
||||
|
||||
let _ = self.0.stream.replace(Some((display.clone(), stream)));
|
||||
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
pub(super) fn create_surface(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
_surface_attribs: &[c_int],
|
||||
output_attribs: &[isize],
|
||||
) -> Result<*const c_void, Error<<<D as Device>::Surface as Surface>::Error>> {
|
||||
let stream = self.create_stream(display, output_attribs)?;
|
||||
|
||||
let (w, h) = self.current_mode().size();
|
||||
info!(self.0.logger, "Creating stream surface with size: ({}:{})", w, h);
|
||||
let surface_attributes = {
|
||||
let mut out: Vec<c_int> = Vec::with_capacity(5);
|
||||
out.push(ffi::egl::WIDTH as i32);
|
||||
out.push(w as i32);
|
||||
out.push(ffi::egl::HEIGHT as i32);
|
||||
out.push(h as i32);
|
||||
out.push(ffi::egl::NONE as i32);
|
||||
out
|
||||
};
|
||||
|
||||
let surface = unsafe {
|
||||
ffi::egl::CreateStreamProducerSurfaceKHR(
|
||||
display.handle,
|
||||
config_id,
|
||||
stream,
|
||||
surface_attributes.as_ptr(),
|
||||
)
|
||||
};
|
||||
if surface == ffi::egl::NO_SURFACE {
|
||||
error!(self.0.logger, "Failed to create surface: 0x{:X}", unsafe {
|
||||
ffi::egl::GetError()
|
||||
});
|
||||
}
|
||||
Ok(surface)
|
||||
}
|
||||
|
||||
pub(super) fn flip(
|
||||
&self,
|
||||
crtc: crtc::Handle,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
) -> Result<(), SwapBuffersError<Error<<<D as Device>::Surface as Surface>::Error>>> {
|
||||
if self.0.locked.load(Ordering::SeqCst) {
|
||||
let acquire_attributes = [
|
||||
ffi::egl::DRM_FLIP_EVENT_DATA_NV as isize,
|
||||
Into::<u32>::into(crtc) as isize,
|
||||
ffi::egl::NONE as isize,
|
||||
];
|
||||
|
||||
if let Ok(stream) = self.0.stream.try_borrow() {
|
||||
let res = if let Some(&(ref display, ref stream)) = stream.as_ref() {
|
||||
wrap_egl_call(|| unsafe {
|
||||
ffi::egl::StreamConsumerAcquireAttribNV(
|
||||
display.handle,
|
||||
*stream,
|
||||
acquire_attributes.as_ptr(),
|
||||
);
|
||||
})
|
||||
.map_err(Error::StreamFlipFailed)
|
||||
} else {
|
||||
Err(Error::StreamFlipFailed(EGLError::NotInitialized))
|
||||
};
|
||||
if res.is_ok() {
|
||||
self.0.locked.store(false, Ordering::SeqCst);
|
||||
} else {
|
||||
return res.map_err(SwapBuffersError::Underlying);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !self.0.locked.load(Ordering::SeqCst) {
|
||||
wrap_egl_call(|| unsafe { ffi::egl::SwapBuffers(***display, surface as *const _) })
|
||||
.map_err(SwapBuffersError::EGLSwapBuffers)?;
|
||||
self.0.locked.store(true, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: RawDevice + 'static> Surface for EglStreamSurface<D> {
|
||||
type Connectors = <<D as Device>::Surface as Surface>::Connectors;
|
||||
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
|
||||
|
||||
fn crtc(&self) -> crtc::Handle {
|
||||
self.0.crtc()
|
||||
}
|
||||
|
||||
fn current_connectors(&self) -> Self::Connectors {
|
||||
self.0.current_connectors()
|
||||
}
|
||||
|
||||
fn pending_connectors(&self) -> Self::Connectors {
|
||||
self.0.pending_connectors()
|
||||
}
|
||||
|
||||
fn add_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
|
||||
self.0.add_connector(connector)
|
||||
}
|
||||
|
||||
fn remove_connector(&self, connector: connector::Handle) -> Result<(), Self::Error> {
|
||||
self.0.remove_connector(connector)
|
||||
}
|
||||
|
||||
fn set_connectors(&self, connectors: &[connector::Handle]) -> Result<(), Self::Error> {
|
||||
self.0.set_connectors(connectors)
|
||||
}
|
||||
|
||||
fn current_mode(&self) -> Mode {
|
||||
self.0.current_mode()
|
||||
}
|
||||
|
||||
fn pending_mode(&self) -> Mode {
|
||||
self.0.pending_mode()
|
||||
}
|
||||
|
||||
fn use_mode(&self, mode: Mode) -> Result<(), Self::Error> {
|
||||
self.0.use_mode(mode)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
impl<D: RawDevice + 'static> CursorBackend for EglStreamSurface<D> {
|
||||
type CursorFormat = ImageBuffer<Rgba<u8>, Vec<u8>>;
|
||||
type Error = Error<DrmError>;
|
||||
|
||||
fn set_cursor_position(&self, x: u32, y: u32) -> Result<(), Self::Error> {
|
||||
self.0.set_cursor_position(x, y)
|
||||
}
|
||||
|
||||
fn set_cursor_representation(
|
||||
&self,
|
||||
buffer: &ImageBuffer<Rgba<u8>, Vec<u8>>,
|
||||
hotspot: (u32, u32),
|
||||
) -> Result<(), Self::Error> {
|
||||
self.0.set_cursor_representation(buffer, hotspot)
|
||||
}
|
||||
}
|
|
@ -5,16 +5,19 @@
|
|||
//!
|
||||
|
||||
use crate::backend::drm::{Device, RawDevice, Surface};
|
||||
use crate::backend::egl::ffi;
|
||||
use crate::backend::egl::native::{Backend, NativeDisplay, NativeSurface};
|
||||
use crate::backend::egl::{wrap_egl_call, EGLError, Error as EglBackendError};
|
||||
use crate::backend::egl::{display::EGLDisplayHandle, ffi};
|
||||
use crate::backend::egl::{
|
||||
wrap_egl_call, EGLError, Error as EglBackendError, SurfaceCreationError, SwapBuffersError,
|
||||
};
|
||||
|
||||
use super::{Error, GbmDevice, GbmSurface};
|
||||
|
||||
use drm::control::{connector, crtc, Device as ControlDevice, Mode};
|
||||
use gbm::AsRaw;
|
||||
use nix::libc::{c_int, c_void};
|
||||
use std::marker::PhantomData;
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Egl Gbm backend type
|
||||
///
|
||||
|
@ -25,9 +28,11 @@ pub struct Gbm<D: RawDevice + 'static> {
|
|||
|
||||
impl<D: RawDevice + 'static> Backend for Gbm<D> {
|
||||
type Surface = GbmSurface<D>;
|
||||
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
|
||||
|
||||
unsafe fn get_display<F>(
|
||||
display: ffi::NativeDisplayType,
|
||||
attribs: &[ffi::EGLint],
|
||||
has_dp_extension: F,
|
||||
log: ::slog::Logger,
|
||||
) -> Result<ffi::egl::types::EGLDisplay, EGLError>
|
||||
|
@ -36,29 +41,33 @@ impl<D: RawDevice + 'static> Backend for Gbm<D> {
|
|||
{
|
||||
if has_dp_extension("EGL_KHR_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() {
|
||||
trace!(log, "EGL Display Initialization via EGL_KHR_platform_gbm");
|
||||
let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, ptr::null())
|
||||
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_KHR, display as *mut _, attribs.as_ptr())
|
||||
})
|
||||
} else if has_dp_extension("EGL_MESA_platform_gbm") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
|
||||
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null())
|
||||
ffi::egl::GetPlatformDisplayEXT(
|
||||
ffi::egl::PLATFORM_GBM_MESA,
|
||||
display as *mut _,
|
||||
attribs.as_ptr(),
|
||||
)
|
||||
})
|
||||
} else if has_dp_extension("EGL_MESA_platform_gbm") && ffi::egl::GetPlatformDisplay::is_loaded() {
|
||||
trace!(log, "EGL Display Initialization via EGL_MESA_platform_gbm");
|
||||
let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, ptr::null())
|
||||
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_GBM_MESA, display as *mut _, attribs.as_ptr())
|
||||
})
|
||||
} else {
|
||||
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
||||
wrap_egl_call(|| ffi::egl::GetDisplay(display as *mut _))
|
||||
Ok(ffi::egl::NO_DISPLAY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<Gbm<D>> for GbmDevice<D> {
|
||||
type Arguments = (crtc::Handle, Mode, Vec<connector::Handle>);
|
||||
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
|
||||
|
||||
fn is_backend(&self) -> bool {
|
||||
true
|
||||
|
@ -68,29 +77,49 @@ unsafe impl<D: RawDevice + ControlDevice + 'static> NativeDisplay<Gbm<D>> for Gb
|
|||
Ok(self.dev.borrow().as_raw() as *const _)
|
||||
}
|
||||
|
||||
fn create_surface(&mut self, args: Self::Arguments) -> Result<GbmSurface<D>, Self::Error> {
|
||||
fn create_surface(
|
||||
&mut self,
|
||||
args: Self::Arguments,
|
||||
) -> Result<GbmSurface<D>, Error<<<D as Device>::Surface as Surface>::Error>> {
|
||||
Device::create_surface(self, args.0, args.1, &args.2)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<D: RawDevice + 'static> NativeSurface for GbmSurface<D> {
|
||||
type Error = Error<<<D as RawDevice>::Surface as Surface>::Error>;
|
||||
type Error = Error<<<D as Device>::Surface as Surface>::Error>;
|
||||
|
||||
fn ptr(&self) -> ffi::NativeWindowType {
|
||||
self.0.surface.borrow().as_raw() as *const _
|
||||
unsafe fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attributes: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Self::Error>> {
|
||||
GbmSurface::recreate(self).map_err(SurfaceCreationError::NativeSurfaceCreationFailed)?;
|
||||
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::CreateWindowSurface(
|
||||
display.handle,
|
||||
config_id,
|
||||
self.0.surface.borrow().as_raw() as *const _,
|
||||
surface_attributes.as_ptr(),
|
||||
)
|
||||
})
|
||||
.map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
|
||||
}
|
||||
|
||||
fn needs_recreation(&self) -> bool {
|
||||
self.needs_recreation()
|
||||
}
|
||||
|
||||
fn recreate(&self) -> Result<(), Self::Error> {
|
||||
GbmSurface::recreate(self)
|
||||
}
|
||||
|
||||
fn swap_buffers(&self) -> Result<(), Self::Error> {
|
||||
fn swap_buffers(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
) -> Result<(), SwapBuffersError<Self::Error>> {
|
||||
wrap_egl_call(|| unsafe { ffi::egl::SwapBuffers(***display, surface as *const _) })
|
||||
.map_err(SwapBuffersError::EGLSwapBuffers)?;
|
||||
// this is safe since `eglSwapBuffers` will have been called exactly once
|
||||
// if this is used by our egl module, which is why this trait is unsafe.
|
||||
unsafe { self.page_flip() }
|
||||
unsafe { self.page_flip() }.map_err(SwapBuffersError::Underlying)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,9 @@ pub struct State {
|
|||
pub connectors: HashSet<connector::Handle>,
|
||||
}
|
||||
|
||||
pub(super) struct LegacyDrmSurfaceInternal<A: AsRawFd + 'static> {
|
||||
pub(in crate::backend::drm) struct LegacyDrmSurfaceInternal<A: AsRawFd + 'static> {
|
||||
pub(super) dev: Rc<Dev<A>>,
|
||||
pub(super) crtc: crtc::Handle,
|
||||
pub(in crate::backend::drm) crtc: crtc::Handle,
|
||||
pub(super) state: RwLock<State>,
|
||||
pub(super) pending: RwLock<State>,
|
||||
pub(super) logger: ::slog::Logger,
|
||||
|
@ -454,7 +454,9 @@ impl<A: AsRawFd + 'static> Drop for LegacyDrmSurfaceInternal<A> {
|
|||
}
|
||||
|
||||
/// Open raw crtc utilizing legacy mode-setting
|
||||
pub struct LegacyDrmSurface<A: AsRawFd + 'static>(pub(super) Rc<LegacyDrmSurfaceInternal<A>>);
|
||||
pub struct LegacyDrmSurface<A: AsRawFd + 'static>(
|
||||
pub(in crate::backend::drm) Rc<LegacyDrmSurfaceInternal<A>>,
|
||||
);
|
||||
|
||||
impl<A: AsRawFd + 'static> AsRawFd for LegacyDrmSurface<A> {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
|
|
|
@ -54,6 +54,8 @@ pub mod atomic;
|
|||
pub mod common;
|
||||
#[cfg(feature = "backend_drm_egl")]
|
||||
pub mod egl;
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
pub mod eglstream;
|
||||
#[cfg(feature = "backend_drm_gbm")]
|
||||
pub mod gbm;
|
||||
#[cfg(feature = "backend_drm_legacy")]
|
||||
|
|
|
@ -8,9 +8,7 @@ use crate::backend::egl::{
|
|||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
use std::ptr;
|
||||
|
||||
use nix::libc::{c_int, c_void};
|
||||
use nix::libc::c_int;
|
||||
|
||||
#[cfg(feature = "wayland_frontend")]
|
||||
use wayland_server::{protocol::wl_buffer::WlBuffer, Display};
|
||||
|
@ -22,7 +20,7 @@ use crate::backend::egl::context::{GlAttributes, PixelFormatRequirements};
|
|||
use crate::backend::graphics::gl::ffi as gl_ffi;
|
||||
use crate::backend::graphics::PixelFormat;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ffi::CStr;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
|
@ -30,8 +28,9 @@ use std::ops::Deref;
|
|||
|
||||
/// Wrapper around [`ffi::EGLDisplay`](ffi::egl::types::EGLDisplay) to ensure display is only destroyed
|
||||
/// once all resources bound to it have been dropped.
|
||||
pub(crate) struct EGLDisplayHandle {
|
||||
handle: ffi::egl::types::EGLDisplay,
|
||||
pub struct EGLDisplayHandle {
|
||||
/// ffi EGLDisplay ptr
|
||||
pub handle: ffi::egl::types::EGLDisplay,
|
||||
}
|
||||
|
||||
impl Deref for EGLDisplayHandle {
|
||||
|
@ -69,29 +68,9 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
{
|
||||
let log = crate::slog_or_stdlog(logger.into()).new(o!("smithay_module" => "renderer_egl"));
|
||||
let ptr = native.ptr()?;
|
||||
let egl_attribs = native.attributes();
|
||||
|
||||
ffi::egl::LOAD.call_once(|| unsafe {
|
||||
fn constrain<F>(f: F) -> F
|
||||
where
|
||||
F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void,
|
||||
{
|
||||
f
|
||||
};
|
||||
|
||||
ffi::egl::load_with(|sym| {
|
||||
let name = CString::new(sym).unwrap();
|
||||
let symbol = ffi::egl::LIB.get::<*mut c_void>(name.as_bytes());
|
||||
match symbol {
|
||||
Ok(x) => *x as *const _,
|
||||
Err(_) => ptr::null(),
|
||||
}
|
||||
});
|
||||
let proc_address = constrain(|sym| get_proc_address(sym));
|
||||
ffi::egl::load_with(&proc_address);
|
||||
ffi::egl::BindWaylandDisplayWL::load_with(&proc_address);
|
||||
ffi::egl::UnbindWaylandDisplayWL::load_with(&proc_address);
|
||||
ffi::egl::QueryWaylandBufferWL::load_with(&proc_address);
|
||||
});
|
||||
ffi::make_sure_egl_is_loaded();
|
||||
|
||||
// the first step is to query the list of extensions without any display, if supported
|
||||
let dp_extensions = unsafe {
|
||||
|
@ -112,9 +91,17 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
debug!(log, "EGL No-Display Extensions: {:?}", dp_extensions);
|
||||
|
||||
let display = unsafe {
|
||||
B::get_display(ptr, |e: &str| dp_extensions.iter().any(|s| s == e), log.clone())
|
||||
.map_err(Error::DisplayNotSupported)?
|
||||
B::get_display(
|
||||
ptr,
|
||||
&egl_attribs,
|
||||
|e: &str| dp_extensions.iter().any(|s| s == e),
|
||||
log.clone(),
|
||||
)
|
||||
.map_err(Error::DisplayCreationError)?
|
||||
};
|
||||
if display == ffi::egl::NO_DISPLAY {
|
||||
return Err(Error::DisplayNotSupported);
|
||||
}
|
||||
|
||||
let egl_version = {
|
||||
let mut major: MaybeUninit<ffi::egl::types::EGLint> = MaybeUninit::uninit();
|
||||
|
@ -174,6 +161,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
) -> Result<(PixelFormat, ffi::egl::types::EGLConfig), Error> {
|
||||
let descriptor = {
|
||||
let mut out: Vec<c_int> = Vec::with_capacity(37);
|
||||
let surface_type = self.native.borrow().surface_type();
|
||||
|
||||
if self.egl_version >= (1, 2) {
|
||||
trace!(self.logger, "Setting COLOR_BUFFER_TYPE to RGB_BUFFER");
|
||||
|
@ -181,12 +169,12 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
out.push(ffi::egl::RGB_BUFFER as c_int);
|
||||
}
|
||||
|
||||
trace!(self.logger, "Setting SURFACE_TYPE to WINDOW");
|
||||
trace!(self.logger, "Setting SURFACE_TYPE to {}", surface_type);
|
||||
|
||||
out.push(ffi::egl::SURFACE_TYPE as c_int);
|
||||
// TODO: Some versions of Mesa report a BAD_ATTRIBUTE error
|
||||
// if we ask for PBUFFER_BIT as well as WINDOW_BIT
|
||||
out.push((ffi::egl::WINDOW_BIT) as c_int);
|
||||
out.push(surface_type);
|
||||
|
||||
match attributes.version {
|
||||
Some((3, _)) => {
|
||||
|
@ -265,11 +253,17 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
config_ids.set_len(num_configs as usize);
|
||||
}
|
||||
|
||||
// TODO: Deeper swap intervals might have some uses
|
||||
if config_ids.is_empty() {
|
||||
return Err(Error::NoAvailablePixelFormat);
|
||||
}
|
||||
|
||||
let desired_swap_interval = if attributes.vsync { 1 } else { 0 };
|
||||
|
||||
let config_ids = config_ids
|
||||
.into_iter()
|
||||
// try to select a config with the desired_swap_interval
|
||||
// (but don't fail, as the margin might be very small on some cards and most configs are fine)
|
||||
let config_id = config_ids
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|config| unsafe {
|
||||
let mut min_swap_interval = 0;
|
||||
wrap_egl_call(|| {
|
||||
|
@ -305,14 +299,8 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
.map_err(Error::ConfigFailed)?
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if config_ids.is_empty() {
|
||||
return Err(Error::NoAvailablePixelFormat);
|
||||
}
|
||||
|
||||
// TODO: Improve config selection
|
||||
let config_id = config_ids[0];
|
||||
.next()
|
||||
.unwrap_or_else(|| config_ids[0]);
|
||||
|
||||
// analyzing each config
|
||||
macro_rules! attrib {
|
||||
|
@ -371,7 +359,7 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
double_buffer: Option<bool>,
|
||||
config: ffi::egl::types::EGLConfig,
|
||||
args: N::Arguments,
|
||||
) -> Result<EGLSurface<B::Surface>, SurfaceCreationError<N::Error>> {
|
||||
) -> Result<EGLSurface<B::Surface>, SurfaceCreationError<B::Error>> {
|
||||
trace!(self.logger, "Creating EGL window surface.");
|
||||
let surface = self
|
||||
.native
|
||||
|
@ -391,7 +379,6 @@ impl<B: native::Backend, N: native::NativeDisplay<B>> EGLDisplay<B, N> {
|
|||
debug!(self.logger, "EGL surface successfully created");
|
||||
x
|
||||
})
|
||||
.map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
|
||||
}
|
||||
|
||||
/// Returns the runtime egl version of this display
|
||||
|
|
|
@ -15,9 +15,12 @@ pub enum Error {
|
|||
/// Backend does not match the context type
|
||||
#[error("The expected backend '{0:?}' does not match the runtime")]
|
||||
NonMatchingBackend(&'static str),
|
||||
/// Display creation failed
|
||||
#[error("Display creation failed with error: {0:}")]
|
||||
DisplayCreationError(#[source] EGLError),
|
||||
/// Unable to obtain a valid EGL Display
|
||||
#[error("Unable to obtain a valid EGL Display. Err: {0:}")]
|
||||
DisplayNotSupported(#[source] EGLError),
|
||||
#[error("Unable to obtain a valid EGL Display.")]
|
||||
DisplayNotSupported,
|
||||
/// `eglInitialize` returned an error
|
||||
#[error("Failed to initialize EGL. Err: {0:}")]
|
||||
InitFailed(#[source] EGLError),
|
||||
|
@ -88,6 +91,10 @@ pub enum EGLError {
|
|||
/// A NativeWindowType argument does not refer to a valid native window.
|
||||
#[error("A NativeWindowType argument does not refer to a valid native window.")]
|
||||
BadNativeWindow,
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
/// The EGL operation failed due to temporary unavailability of a requested resource, but the arguments were otherwise valid, and a subsequent attempt may succeed.
|
||||
#[error("The EGL operation failed due to temporary unavailability of a requested resource, but the arguments were otherwise valid, and a subsequent attempt may succeed.")]
|
||||
ResourceBusy,
|
||||
/// A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering.
|
||||
#[error("A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering.")]
|
||||
ContextLost,
|
||||
|
@ -111,6 +118,8 @@ impl From<u32> for EGLError {
|
|||
ffi::egl::BAD_PARAMETER => EGLError::BadParameter,
|
||||
ffi::egl::BAD_NATIVE_PIXMAP => EGLError::BadNativePixmap,
|
||||
ffi::egl::BAD_NATIVE_WINDOW => EGLError::BadNativeWindow,
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
ffi::egl::RESOURCE_BUSY_EXT => EGLError::ResourceBusy,
|
||||
ffi::egl::CONTEXT_LOST => EGLError::ContextLost,
|
||||
x => EGLError::Unknown(x),
|
||||
}
|
||||
|
|
|
@ -13,6 +13,35 @@ pub type NativeDisplayType = *const c_void;
|
|||
pub type NativePixmapType = *const c_void;
|
||||
pub type NativeWindowType = *const c_void;
|
||||
|
||||
pub fn make_sure_egl_is_loaded() {
|
||||
use std::{ffi::CString, ptr};
|
||||
|
||||
egl::LOAD.call_once(|| unsafe {
|
||||
fn constrain<F>(f: F) -> F
|
||||
where
|
||||
F: for<'a> Fn(&'a str) -> *const ::std::os::raw::c_void,
|
||||
{
|
||||
f
|
||||
};
|
||||
|
||||
egl::load_with(|sym| {
|
||||
let name = CString::new(sym).unwrap();
|
||||
let symbol = egl::LIB.get::<*mut c_void>(name.as_bytes());
|
||||
match symbol {
|
||||
Ok(x) => *x as *const _,
|
||||
Err(_) => ptr::null(),
|
||||
}
|
||||
});
|
||||
let proc_address = constrain(|sym| super::get_proc_address(sym));
|
||||
egl::load_with(&proc_address);
|
||||
egl::BindWaylandDisplayWL::load_with(&proc_address);
|
||||
egl::UnbindWaylandDisplayWL::load_with(&proc_address);
|
||||
egl::QueryWaylandBufferWL::load_with(&proc_address);
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
egl::StreamConsumerAcquireAttribNV::load_with(&proc_address);
|
||||
});
|
||||
}
|
||||
|
||||
#[allow(clippy::all, rust_2018_idioms)]
|
||||
pub mod egl {
|
||||
use super::*;
|
||||
|
@ -169,4 +198,66 @@ pub mod egl {
|
|||
// Accepted in the <attribute> parameter of eglQueryWaylandBufferWL:
|
||||
pub const EGL_TEXTURE_FORMAT: i32 = 0x3080;
|
||||
pub const WAYLAND_Y_INVERTED_WL: i32 = 0x31DB;
|
||||
|
||||
/// nVidia support needs some implemented but only proposed egl extensions...
|
||||
/// Therefor gl_generator cannot generate them and we need some constants...
|
||||
/// And a function...
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
pub const CONSUMER_AUTO_ACQUIRE_EXT: i32 = 0x332B;
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
pub const DRM_FLIP_EVENT_DATA_NV: i32 = 0x333E;
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
pub const CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR: i32 = 0x321E;
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
pub const RESOURCE_BUSY_EXT: u32 = 0x3353;
|
||||
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
#[allow(non_snake_case, unused_variables, dead_code)]
|
||||
#[inline]
|
||||
pub unsafe fn StreamConsumerAcquireAttribNV(
|
||||
dpy: types::EGLDisplay,
|
||||
stream: types::EGLStreamKHR,
|
||||
attrib_list: *const types::EGLAttrib,
|
||||
) -> types::EGLBoolean {
|
||||
__gl_imports::mem::transmute::<
|
||||
_,
|
||||
extern "system" fn(
|
||||
types::EGLDisplay,
|
||||
types::EGLStreamKHR,
|
||||
*const types::EGLAttrib,
|
||||
) -> types::EGLBoolean,
|
||||
>(nvidia_storage::StreamConsumerAcquireAttribNV.f)(dpy, stream, attrib_list)
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
mod nvidia_storage {
|
||||
use super::{FnPtr, __gl_imports::raw};
|
||||
pub static mut StreamConsumerAcquireAttribNV: FnPtr = FnPtr {
|
||||
f: super::missing_fn_panic as *const raw::c_void,
|
||||
is_loaded: false,
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_drm_eglstream")]
|
||||
#[allow(non_snake_case)]
|
||||
pub mod StreamConsumerAcquireAttribNV {
|
||||
use super::{FnPtr, __gl_imports::raw, metaloadfn, nvidia_storage};
|
||||
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub fn is_loaded() -> bool {
|
||||
unsafe { nvidia_storage::StreamConsumerAcquireAttribNV.is_loaded }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn load_with<F>(mut loadfn: F)
|
||||
where
|
||||
F: FnMut(&str) -> *const raw::c_void,
|
||||
{
|
||||
unsafe {
|
||||
nvidia_storage::StreamConsumerAcquireAttribNV =
|
||||
FnPtr::new(metaloadfn(&mut loadfn, "eglStreamConsumerAcquireAttribNV", &[]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
//! Type safe native types for safe context/surface creation
|
||||
|
||||
use super::{ffi, wrap_egl_call, EGLError, Error};
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
use std::ptr;
|
||||
use super::{
|
||||
display::EGLDisplayHandle, ffi, wrap_egl_call, EGLError, Error, SurfaceCreationError, SwapBuffersError,
|
||||
};
|
||||
use nix::libc::{c_int, c_void};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
use wayland_egl as wegl;
|
||||
|
@ -15,7 +16,9 @@ use winit::window::Window as WinitWindow;
|
|||
/// Trait for typed backend variants (X11/Wayland/GBM)
|
||||
pub trait Backend {
|
||||
/// Surface type created by this backend
|
||||
type Surface: NativeSurface;
|
||||
type Surface: NativeSurface<Error = Self::Error>;
|
||||
/// Error type thrown by the surface creation in case of failure.
|
||||
type Error: ::std::error::Error + Send + 'static;
|
||||
|
||||
/// Return an [`EGLDisplay`](ffi::egl::types::EGLDisplay) based on this backend
|
||||
///
|
||||
|
@ -25,6 +28,7 @@ pub trait Backend {
|
|||
/// but there is no way to test that.
|
||||
unsafe fn get_display<F: Fn(&str) -> bool>(
|
||||
display: ffi::NativeDisplayType,
|
||||
attribs: &[ffi::EGLint],
|
||||
has_dp_extension: F,
|
||||
log: ::slog::Logger,
|
||||
) -> Result<ffi::egl::types::EGLDisplay, EGLError>;
|
||||
|
@ -36,9 +40,11 @@ pub enum Wayland {}
|
|||
#[cfg(feature = "backend_winit")]
|
||||
impl Backend for Wayland {
|
||||
type Surface = wegl::WlEglSurface;
|
||||
type Error = Error;
|
||||
|
||||
unsafe fn get_display<F>(
|
||||
display: ffi::NativeDisplayType,
|
||||
attribs: &[ffi::EGLint],
|
||||
has_dp_extension: F,
|
||||
log: ::slog::Logger,
|
||||
) -> Result<ffi::egl::types::EGLDisplay, EGLError>
|
||||
|
@ -47,8 +53,13 @@ impl Backend for Wayland {
|
|||
{
|
||||
if has_dp_extension("EGL_KHR_platform_wayland") && ffi::egl::GetPlatformDisplay::is_loaded() {
|
||||
trace!(log, "EGL Display Initialization via EGL_KHR_platform_wayland");
|
||||
let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_WAYLAND_KHR, display as *mut _, ptr::null())
|
||||
ffi::egl::GetPlatformDisplay(
|
||||
ffi::egl::PLATFORM_WAYLAND_KHR,
|
||||
display as *mut _,
|
||||
attribs.as_ptr(),
|
||||
)
|
||||
})
|
||||
} else if has_dp_extension("EGL_EXT_platform_wayland") && ffi::egl::GetPlatformDisplayEXT::is_loaded()
|
||||
{
|
||||
|
@ -57,7 +68,7 @@ impl Backend for Wayland {
|
|||
ffi::egl::GetPlatformDisplayEXT(
|
||||
ffi::egl::PLATFORM_WAYLAND_EXT,
|
||||
display as *mut _,
|
||||
ptr::null(),
|
||||
attribs.as_ptr(),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
|
@ -76,9 +87,11 @@ pub enum X11 {}
|
|||
#[cfg(feature = "backend_winit")]
|
||||
impl Backend for X11 {
|
||||
type Surface = XlibWindow;
|
||||
type Error = Error;
|
||||
|
||||
unsafe fn get_display<F>(
|
||||
display: ffi::NativeDisplayType,
|
||||
attribs: &[ffi::EGLint],
|
||||
has_dp_extension: F,
|
||||
log: ::slog::Logger,
|
||||
) -> Result<ffi::egl::types::EGLDisplay, EGLError>
|
||||
|
@ -87,13 +100,18 @@ impl Backend for X11 {
|
|||
{
|
||||
if has_dp_extension("EGL_KHR_platform_x11") && ffi::egl::GetPlatformDisplay::is_loaded() {
|
||||
trace!(log, "EGL Display Initialization via EGL_KHR_platform_x11");
|
||||
let attribs = attribs.iter().map(|x| *x as isize).collect::<Vec<_>>();
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, ptr::null())
|
||||
ffi::egl::GetPlatformDisplay(ffi::egl::PLATFORM_X11_KHR, display as *mut _, attribs.as_ptr())
|
||||
})
|
||||
} else if has_dp_extension("EGL_EXT_platform_x11") && ffi::egl::GetPlatformDisplayEXT::is_loaded() {
|
||||
trace!(log, "EGL Display Initialization via EGL_EXT_platform_x11");
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::GetPlatformDisplayEXT(ffi::egl::PLATFORM_X11_EXT, display as *mut _, ptr::null())
|
||||
ffi::egl::GetPlatformDisplayEXT(
|
||||
ffi::egl::PLATFORM_X11_EXT,
|
||||
display as *mut _,
|
||||
attribs.as_ptr(),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
trace!(log, "Default EGL Display Initialization via GetDisplay");
|
||||
|
@ -110,21 +128,28 @@ impl Backend for X11 {
|
|||
pub unsafe trait NativeDisplay<B: Backend> {
|
||||
/// Arguments used to surface creation.
|
||||
type Arguments;
|
||||
/// Error type thrown by the surface creation in case of failure.
|
||||
type Error: ::std::error::Error + Send + 'static;
|
||||
/// Because one type might implement multiple [`Backend`]s this function must be called to check
|
||||
/// if the expected [`Backend`] is used at runtime.
|
||||
fn is_backend(&self) -> bool;
|
||||
/// Return a raw pointer EGL will accept for context creation.
|
||||
fn ptr(&self) -> Result<ffi::NativeDisplayType, Error>;
|
||||
/// Return attributes that might be used by `B::get_display`
|
||||
///
|
||||
/// Default implementation returns an empty list
|
||||
fn attributes(&self) -> Vec<ffi::EGLint> {
|
||||
vec![ffi::egl::NONE as ffi::EGLint]
|
||||
}
|
||||
/// Type of surfaces created
|
||||
fn surface_type(&self) -> ffi::EGLint {
|
||||
ffi::egl::WINDOW_BIT as ffi::EGLint
|
||||
}
|
||||
/// Create a surface
|
||||
fn create_surface(&mut self, args: Self::Arguments) -> Result<B::Surface, Self::Error>;
|
||||
fn create_surface(&mut self, args: Self::Arguments) -> Result<B::Surface, B::Error>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
unsafe impl NativeDisplay<X11> for WinitWindow {
|
||||
type Arguments = ();
|
||||
type Error = Error;
|
||||
|
||||
fn is_backend(&self) -> bool {
|
||||
self.xlib_display().is_some()
|
||||
|
@ -146,7 +171,6 @@ unsafe impl NativeDisplay<X11> for WinitWindow {
|
|||
#[cfg(feature = "backend_winit")]
|
||||
unsafe impl NativeDisplay<Wayland> for WinitWindow {
|
||||
type Arguments = ();
|
||||
type Error = Error;
|
||||
|
||||
fn is_backend(&self) -> bool {
|
||||
self.wayland_display().is_some()
|
||||
|
@ -177,67 +201,88 @@ unsafe impl NativeDisplay<Wayland> for WinitWindow {
|
|||
/// The returned [`NativeWindowType`](ffi::NativeWindowType) must be valid for EGL
|
||||
/// and there is no way to test that.
|
||||
pub unsafe trait NativeSurface {
|
||||
/// Error of the underlying surface
|
||||
type Error: std::error::Error;
|
||||
|
||||
/// Return a raw pointer egl will accept for surface creation.
|
||||
fn ptr(&self) -> ffi::NativeWindowType;
|
||||
/// Error type thrown by the surface creation in case of failure.
|
||||
type Error: ::std::error::Error + Send + 'static;
|
||||
/// Create an EGLSurface from the internal native type.
|
||||
///
|
||||
/// Must be able to deal with re-creation of existing resources,
|
||||
/// if `needs_recreation` can return `true`.
|
||||
///
|
||||
/// # Safety
|
||||
/// This is usually an unsafe operation returning a raw pointer.
|
||||
unsafe fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attributes: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Self::Error>>;
|
||||
|
||||
/// Will be called to check if any internal resources will need
|
||||
/// to be recreated. Old resources must be used until `recreate`
|
||||
/// was called.
|
||||
/// to be recreated. Old resources must be used until `create`
|
||||
/// was called again and a new surface was optained.
|
||||
///
|
||||
/// Only needs to be recreated, if this shall sometimes return true.
|
||||
/// Only needs to be recreated, if this may return true.
|
||||
/// The default implementation always returns false.
|
||||
fn needs_recreation(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Instructs the surface to recreate internal resources
|
||||
///
|
||||
/// Must only be implemented if `needs_recreation` can return `true`.
|
||||
/// Returns true on success.
|
||||
/// If this call was successful `ptr()` *should* return something different.
|
||||
fn recreate(&self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Adds additional semantics when calling
|
||||
/// [EGLSurface::swap_buffers](::backend::egl::surface::EGLSurface::swap_buffers)
|
||||
///
|
||||
/// Only implement if required by the backend.
|
||||
fn swap_buffers(&self) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
fn swap_buffers(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
surface: ffi::egl::types::EGLSurface,
|
||||
) -> Result<(), SwapBuffersError<Self::Error>> {
|
||||
wrap_egl_call(|| unsafe {
|
||||
ffi::egl::SwapBuffers(***display, surface as *const _);
|
||||
})
|
||||
.map_err(SwapBuffersError::EGLSwapBuffers)
|
||||
}
|
||||
}
|
||||
|
||||
/// Hack until ! gets stablized
|
||||
#[derive(Debug)]
|
||||
pub enum Never {}
|
||||
impl std::fmt::Display for Never {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
impl std::error::Error for Never {}
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
unsafe impl NativeSurface for XlibWindow {
|
||||
// this would really be a case for this:
|
||||
// type Error = !; (https://github.com/rust-lang/rust/issues/35121)
|
||||
type Error = Never;
|
||||
type Error = Error;
|
||||
|
||||
fn ptr(&self) -> ffi::NativeWindowType {
|
||||
self.0 as *const _
|
||||
unsafe fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attributes: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Error>> {
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::CreateWindowSurface(
|
||||
display.handle,
|
||||
config_id,
|
||||
self.0 as *const _,
|
||||
surface_attributes.as_ptr(),
|
||||
)
|
||||
})
|
||||
.map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "backend_winit")]
|
||||
unsafe impl NativeSurface for wegl::WlEglSurface {
|
||||
// type Error = !;
|
||||
type Error = Never;
|
||||
type Error = Error;
|
||||
|
||||
fn ptr(&self) -> ffi::NativeWindowType {
|
||||
self.ptr() as *const _
|
||||
unsafe fn create(
|
||||
&self,
|
||||
display: &Arc<EGLDisplayHandle>,
|
||||
config_id: ffi::egl::types::EGLConfig,
|
||||
surface_attributes: &[c_int],
|
||||
) -> Result<*const c_void, SurfaceCreationError<Error>> {
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::CreateWindowSurface(
|
||||
display.handle,
|
||||
config_id,
|
||||
self.ptr() as *const _,
|
||||
surface_attributes.as_ptr(),
|
||||
)
|
||||
})
|
||||
.map_err(SurfaceCreationError::EGLSurfaceCreationFailed)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! EGL surface related structs
|
||||
|
||||
use super::{ffi, native, wrap_egl_call, EGLError, SwapBuffersError};
|
||||
use super::{ffi, native, EGLError, SurfaceCreationError, SwapBuffersError};
|
||||
use crate::backend::egl::display::EGLDisplayHandle;
|
||||
use crate::backend::graphics::PixelFormat;
|
||||
use nix::libc::c_int;
|
||||
|
@ -42,7 +42,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
config: ffi::egl::types::EGLConfig,
|
||||
native: N,
|
||||
log: L,
|
||||
) -> Result<EGLSurface<N>, EGLError>
|
||||
) -> Result<EGLSurface<N>, SurfaceCreationError<N::Error>>
|
||||
where
|
||||
L: Into<Option<::slog::Logger>>,
|
||||
{
|
||||
|
@ -69,12 +69,12 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
out
|
||||
};
|
||||
|
||||
let surface = wrap_egl_call(|| unsafe {
|
||||
ffi::egl::CreateWindowSurface(**display, config, native.ptr(), surface_attributes.as_ptr())
|
||||
})?;
|
||||
let surface = unsafe { native.create(&display, config, &surface_attributes)? };
|
||||
|
||||
if surface == ffi::egl::NO_SURFACE {
|
||||
return Err(EGLError::BadSurface);
|
||||
return Err(SurfaceCreationError::EGLSurfaceCreationFailed(
|
||||
EGLError::BadSurface,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(EGLSurface {
|
||||
|
@ -93,9 +93,7 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
let surface = self.surface.get();
|
||||
|
||||
let result = if !surface.is_null() {
|
||||
wrap_egl_call(|| unsafe { ffi::egl::SwapBuffers(**self.display, surface as *const _) })
|
||||
.map_err(SwapBuffersError::EGLSwapBuffers)
|
||||
.and_then(|_| self.native.swap_buffers().map_err(SwapBuffersError::Underlying))
|
||||
self.native.swap_buffers(&self.display, surface)
|
||||
} else {
|
||||
Err(SwapBuffersError::EGLSwapBuffers(EGLError::BadSurface))
|
||||
};
|
||||
|
@ -108,20 +106,20 @@ impl<N: native::NativeSurface> EGLSurface<N> {
|
|||
};
|
||||
|
||||
if self.native.needs_recreation() || surface.is_null() || is_bad_surface {
|
||||
self.native.recreate().map_err(SwapBuffersError::Underlying)?;
|
||||
if !surface.is_null() {
|
||||
let _ = unsafe { ffi::egl::DestroySurface(**self.display, surface as *const _) };
|
||||
}
|
||||
self.surface.set(unsafe {
|
||||
wrap_egl_call(|| {
|
||||
ffi::egl::CreateWindowSurface(
|
||||
**self.display,
|
||||
self.config_id,
|
||||
self.native.ptr(),
|
||||
self.surface_attributes.as_ptr(),
|
||||
)
|
||||
})
|
||||
.map_err(SwapBuffersError::EGLCreateWindowSurface)?
|
||||
self.native
|
||||
.create(&self.display, self.config_id, &self.surface_attributes)
|
||||
.map_err(|err| match err {
|
||||
SurfaceCreationError::EGLSurfaceCreationFailed(err) => {
|
||||
SwapBuffersError::EGLCreateWindowSurface(err)
|
||||
}
|
||||
SurfaceCreationError::NativeSurfaceCreationFailed(err) => {
|
||||
SwapBuffersError::Underlying(err)
|
||||
}
|
||||
})?
|
||||
});
|
||||
|
||||
result.map_err(|err| {
|
||||
|
|
|
@ -32,7 +32,7 @@ pub enum SwapBuffersError {
|
|||
///
|
||||
/// Operations will have no effect. Functions that read textures, buffers, etc.
|
||||
/// will return uninitialized data instead.
|
||||
#[error("The context has been lost, it needs to be recreated")]
|
||||
#[error("The context has been lost, it needs to be recreated: {0}")]
|
||||
ContextLost(Box<dyn std::error::Error>),
|
||||
/// A temporary condition caused to rendering to fail.
|
||||
///
|
||||
|
@ -43,6 +43,6 @@ pub enum SwapBuffersError {
|
|||
/// Proceed after investigating the source to reschedule another full rendering step or just this page_flip at a later time.
|
||||
/// If the root cause cannot be discovered and subsequent renderings also fail, it is advised to fallback to
|
||||
/// recreation.
|
||||
#[error("A temporary condition caused the page flip to fail.")]
|
||||
#[error("A temporary condition caused the page flip to fail: {0}")]
|
||||
TemporaryFailure(Box<dyn std::error::Error>),
|
||||
}
|
||||
|
|
|
@ -256,3 +256,24 @@ pub fn all_gpus<S: AsRef<str>>(seat: S) -> IoResult<Vec<PathBuf>> {
|
|||
.flat_map(|device| device.devnode().map(PathBuf::from))
|
||||
.collect())
|
||||
}
|
||||
|
||||
/// Returns the loaded driver for a device named by it's [`dev_t`](::nix::sys::stat::dev_t).
|
||||
pub fn driver(dev: dev_t) -> IoResult<Option<OsString>> {
|
||||
let mut enumerator = Enumerator::new()?;
|
||||
enumerator.match_subsystem("drm")?;
|
||||
enumerator.match_sysname("card[0-9]*")?;
|
||||
Ok(enumerator
|
||||
.scan_devices()?
|
||||
.filter(|device| device.devnum() == Some(dev))
|
||||
.flat_map(|dev| {
|
||||
let mut device = Some(dev);
|
||||
while let Some(dev) = device {
|
||||
if dev.driver().is_some() {
|
||||
return dev.driver().map(std::ffi::OsStr::to_os_string);
|
||||
}
|
||||
device = dev.parent();
|
||||
}
|
||||
None
|
||||
})
|
||||
.next())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue