x11: Initialize via egl instead of dri3 if possible

This commit is contained in:
Victor Brekenfeld 2021-11-04 00:24:02 +01:00 committed by Victor Brekenfeld
parent e4891b0c9e
commit 42b646a152
2 changed files with 95 additions and 2 deletions

View File

@ -38,7 +38,7 @@ rand = "0.8.4"
slog = "2"
slog-stdlog = { version = "4", optional = true }
tempfile = { version = "3.0", optional = true }
thiserror = "1.0.2"
thiserror = "1.0.7"
udev = { version = "0.6", optional = true }
wayland-commons = { version = "0.29.0", optional = true }
wayland-egl = { version = "0.29.0", optional = true }

View File

@ -76,6 +76,7 @@ use crate::{
backend::{
allocator::dmabuf::{AsDmabuf, Dmabuf},
drm::{node::path_to_type, CreateDrmNodeError, DrmNode, NodeType},
egl::{native::X11DefaultDisplay, EGLDevice, EGLDisplay, Error as EGLError},
input::{Axis, ButtonState, InputEvent, KeyState},
},
utils::{x11rb::X11Source, Logical, Size},
@ -322,7 +323,14 @@ impl X11Backend {
// We cannot fallback on the egl_init method, because there is no way for us to authenticate a primary node.
// dri3 does not work for closed-source drivers, but *may* give us a authenticated fd as a fallback.
// As a result we try to use egl for a cleaner, better supported approach at first and only if that fails use dri3.
todo!()
egl_init(&self).or_else(|err| {
slog::warn!(
&self.log,
"Failed to init X11 surface via egl, falling back to dri3: {}",
err
);
dri3_init(&self)
})
}
}
@ -340,6 +348,91 @@ pub struct X11Surface {
next: Dmabuf,
}
#[derive(Debug, thiserror::Error)]
enum EGLInitError {
#[error(transparent)]
EGL(#[from] EGLError),
#[error(transparent)]
IO(#[from] io::Error),
}
fn egl_init(_backend: &X11Backend) -> Result<DrmNode, EGLInitError> {
let display = EGLDisplay::new(&X11DefaultDisplay, None)?;
let device = EGLDevice::device_for_display(&display)?;
let path = path_to_type(device.drm_device_path()?, NodeType::Render)?;
fcntl::open(&path, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
.map_err(Into::<io::Error>::into)
.and_then(|fd| {
DrmNode::from_fd(fd).map_err(|err| match err {
CreateDrmNodeError::Io(err) => err,
_ => unreachable!(),
})
})
.map_err(EGLInitError::IO)
}
fn dri3_init(backend: &X11Backend) -> Result<DrmNode, X11Error> {
let connection = &backend.connection;
// Determine which drm-device the Display is using.
let screen = &connection.setup().roots[backend.screen()];
// provider being NONE tells the X server to use the RandR provider.
let dri3 = match connection.dri3_open(screen.root, x11rb::NONE)?.reply() {
Ok(reply) => reply,
Err(err) => {
return Err(if let ReplyError::X11Error(ref protocol_error) = err {
match protocol_error.error_kind {
// Implementation is risen when the renderer is not capable of X server is not capable
// of rendering at all.
ErrorKind::Implementation => X11Error::CannotDirectRender,
// Match may occur when the node cannot be authenticated for the client.
ErrorKind::Match => X11Error::CannotDirectRender,
_ => err.into(),
}
} else {
err.into()
});
}
};
// Take ownership of the container's inner value so we do not need to duplicate the fd.
// This is fine because the X server will always open a new file descriptor.
let drm_device_fd = dri3.device_fd.into_raw_fd();
let fd_flags =
fcntl::fcntl(drm_device_fd.as_raw_fd(), fcntl::F_GETFD).map_err(AllocateBuffersError::from)?;
// Enable the close-on-exec flag.
fcntl::fcntl(
drm_device_fd,
fcntl::F_SETFD(fcntl::FdFlag::from_bits_truncate(fd_flags) | fcntl::FdFlag::FD_CLOEXEC),
)
.map_err(AllocateBuffersError::from)?;
let drm_node = DrmNode::from_fd(drm_device_fd).map_err(Into::<AllocateBuffersError>::into)?;
if drm_node.ty() != NodeType::Render {
if drm_node.has_render() {
// Try to get the render node.
if let Some(path) = drm_node.dev_path_with_type(NodeType::Render) {
return Ok(fcntl::open(&path, OFlag::O_RDWR | OFlag::O_CLOEXEC, Mode::empty())
.map_err(Into::<std::io::Error>::into)
.map_err(CreateDrmNodeError::Io)
.and_then(DrmNode::from_fd)
.unwrap_or_else(|err| {
slog::warn!(&backend.log, "Could not create render node from existing DRM node ({}), falling back to primary node", err);
drm_node
}));
}
}
}
slog::warn!(
&backend.log,
"DRM Device does not have a render node, falling back to primary node"
);
Ok(drm_node)
}
impl X11Surface {
/// Creates a surface that allocates and presents buffers to the window managed by the backend.
///